/******************************************************************************
 *                                                                            *
 *                       (C) 1997-1999 ESS Technology, Inc.                   *
 *                                                                            *
 * This source code, its compiled object code, and its associated data sets   *
 * are copyright (C) 1997-1999 ESS Technology, Inc. 			      *
 *                                                                            *
 ******************************************************************************/

/*---------------------------------------------------------------------------
 *              Copyright (C) 1997-1999, ESS Technology, Inc.
 *---------------------------------------------------------------------------
 * FILENAME: kernel.c V2.10  08/19/99
 *---------------------------------------------------------------------------
 * DESCRIPTION: Maestro 3/Allegro1 host kernel
 *---------------------------------------------------------------------------
 * AUTHOR:  Henry Tang/Hong Kim/Alger Yeung/Don Kim
 *---------------------------------------------------------------------------
 * HISTORY:
 *    09/03/97  HT  Created.
 *    04/23/99  AY  Use C call for lowest level access (in and out)
 *    04/23/99  AY  Remove delay since M3/Allegro1 has no delay bug
 *    04/23/99  AY  Cleanup dead code
 *    05/08/99  AY  SPDIF support SCMS
 *    05/21/99  AY  Add SwitchClient API to add/remove client from/to task list
 *    05/24/99  AY  Enable Cpythru (dwclient=0) to be loaded even though
 *                  its code memory is not available at certain location
 *    06/02/99  AY  Enable PassThru support for adc1 -> mixer & adc2->mixer
 *    07/29/99  AY  Adding 4-speaker support
 *    08/18/99  AY  Adding SPDIF IN  support
 *    08/18/99  AY  Remove PIO and SoundBlaster support
 *    08/18/99  AY  Reduce Cpythru to 2 instances instead of 4
 *    09/22/99  HK  Add M3I Features
 *---------------------------------------------------------------------------
 */

#define NON_INTEL  1		/* avoid Intel x86 inline assembly instruction */
/*#define ALLEGRO_DEBUG 1      // KERNEL debug flag */
/*#define K_DBG 1      // KERNEL debug flag */

#ifdef NT_MODEL
#define  HEAPZEROINIT   0
#ifdef DON
#include "../port.h"
#include "kernel.h"
#endif
#endif

#ifdef NON_INTEL
/*#error ******** No Intel x86 assembly instructions ************* */
#endif

PCLIENT_BIN kBinStructAddress (PHWI phwi, DWORD dwClient, DWORD dwSearchKey);

/* */
/* Client info */
/* */

HWI ghwi = {

  0,
  0,
  0,
  0,

  0,

  0, 0,

  0,

  /* client table */

  {
   {
    gasCpyThruVectCode,
    0,
    MAX_INSTANCE_CPYTHRU,
    KDATA_INSTANCE0_CPYTHRU,
    0,
    0,
    0}
   ,

   {
    gasModemVectCode,
    0,
    MAX_INSTANCE_MODEM,
    KDATA_INSTANCE0_MODEM,
    0,
    0,
    0}
   ,

   {
    gasPos3DVectCode,
    0,
    MAX_INSTANCE_POS3D,
    KDATA_INSTANCE0_POS3D,
    0,
    0,
    0}
   ,

   {
    gasSpkVirtVectCode,
    0,
    MAX_INSTANCE_SPKVIRT,
    KDATA_INSTANCE0_SPKVIRT,
    0,
    0,
    0}
   ,

   {
    gasSpkVirtVectCode_CRL,
    0,
    MAX_INSTANCE_SPKVIRT,
    KDATA_INSTANCE0_SPKVIRT,
    0,
    0,
    0}
   ,

   {
    gasSRCVectCode,
    0,
    MAX_INSTANCE_SRC,
    KDATA_INSTANCE0_SRC,
    0,
    0,
    0}
   ,

   {
    gasMINISRCVectCode,
    0,
    MAX_INSTANCE_MINISRC,
    KDATA_INSTANCE0_MINISRC,
    0,
    0,
    0}
   ,

   {
    gasSPDIFVectCode,
    0,
    MAX_INSTANCE_SPDIF,
    KDATA_INSTANCE0_SPDIF,
    0,
    0,
    0}

   }
  ,
#if 0
#if (F_FREE || (F_END != -1))
#error Assumption about storage flags failed.
#endif
#endif
  /* task resource list */

  {
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,

   (WORD) F_END}
  ,

  /* Copy Through resource list */

  {
   F_FREE, F_FREE,		/* AY reduce to 2 for SPDIF IN */

   (WORD) F_END}
  ,

  /* Modem resource list */

  {
   F_FREE,

   (WORD) F_END}
  ,

  /* Positional 3D resource list */

  {
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE,

   (WORD) F_END}
  ,

  /* Speaker Virtualization resource list */

  {
   F_FREE,

   (WORD) F_END}
  ,

  /* Sample Rate Conversion resource list */

  {
   F_FREE, F_FREE,

   (WORD) F_END}
  ,

  /* MINI Sample Rate Conversion resource list */

  {
   F_FREE, F_FREE,
   F_FREE, F_FREE,

   (WORD) F_END}
  ,

  /* SPDIF resource list */

  {
   F_FREE,

   (WORD) F_END}
  ,

  /* DMA resource list */

  {
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE,

   (WORD) F_END}
  ,

  /* ADC1 resource list */

  {
   F_FREE,

   (WORD) F_END}
  ,

  /* ADC2 resource list */

  {
   F_FREE,

   (WORD) F_END}
  ,

  /* CD resource list */
  {
   F_FREE,

   (WORD) F_END}
  ,

  /* MIC resource list */

  {
   F_FREE,

   (WORD) F_END}
  ,

  /* I2S resource list */

  {
   F_FREE,

   (WORD) F_END}
  ,

  /* CHI resource list */

  {
   F_FREE,

   (WORD) F_END}
  ,


  /* SPDIF IN resource list */

  {
   F_FREE,

   (WORD) F_END}
  ,

  /* MIXER resource list */
  {
   F_FREE, F_FREE,
   F_FREE, F_FREE,
   F_FREE, F_FREE,
   F_FREE, F_FREE,
   F_FREE, F_FREE,

   (WORD) F_END}
  ,

  /*AY */
  /* FMIXER resource list */
  {
   F_FREE,

   (WORD) F_END}
  ,

  /* RMIXER resource list */
  {
   F_FREE,

   (WORD) F_END}
  ,

  /* DSP code memory map */

  0, 0, 0, 0,

  0, 0, 0, 0,

  {
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,

   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,

   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,

   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,

   (BYTE) F_END}
  ,

  /* DSP data memory map */

  {
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,

   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,

   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,

   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,

   (BYTE) F_END}
  ,

  /* DSP vector list */

  {
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,

   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,

   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE,
   F_FREE, F_FREE, F_FREE, F_FREE}

};

/* */
/* Memory map images */
/* */
#if 0
#if (NUM_UNITS_KERNEL_CODE != 16)
#error Assumption about kernel code size failed.
#endif
#endif

BYTE gabRevBCodeMemoryMapImage[] = {
  F_USED, F_USED, F_USED, F_USED,	/* 0000 - 03FF */
  F_USED, F_USED, F_USED, F_USED,
  F_USED, F_USED, F_USED, F_USED,
  F_USED, F_USED, F_USED, F_USED,

  F_FREE, F_FREE, F_FREE, F_FREE,	/* 0400 - 07FF */
  F_FREE, F_FREE, F_FREE, F_FREE,
  F_FREE, F_FREE, F_FREE, F_FREE,
  F_FREE, F_FREE, F_FREE, F_FREE,

  F_FREE, F_FREE, F_FREE, F_FREE,	/* 0800 - 0BFF */
  F_FREE, F_FREE, F_FREE, F_FREE,
  F_FREE, F_FREE, F_FREE, F_FREE,
  F_FREE, F_FREE, F_FREE, F_FREE,

  (BYTE) F_END
};

#if 0
#if (NUM_UNITS_KERNEL_DATA != 2)
#error Assumption about kernel data size failed.
#endif
#endif

#if 0
#if (KDATA_BASE_ADDR != 0x1000)
#error Assumption about kernel data memory location failed.
#endif
#endif

BYTE gabRevBDataMemoryMapImage[] = {
  F_USED, F_USED, F_FREE, F_FREE,	/* 1000 - 17FF */
  F_FREE, F_FREE, F_FREE, F_FREE,
  F_FREE, F_FREE, F_FREE, F_FREE,
  F_FREE, F_FREE, F_FREE, F_FREE,

  F_FREE, F_FREE, F_FREE, F_FREE,	/* 1800 - 1BFF */
  F_FREE, F_FREE, F_FREE, F_FREE,

  F_USED, F_USED, F_USED, F_USED,	/* 1C00 - 1FFF */
  F_USED, F_USED, F_USED, F_USED,

  F_USED, F_USED, F_USED, F_USED,	/* 2000 - 27FF */
  F_USED, F_USED, F_USED, F_USED,
  F_USED, F_USED, F_USED, F_USED,
  F_USED, F_USED, F_USED, F_USED,

  F_USED, F_USED, F_USED, F_USED,	/* 2800 - 2BFF */
  F_USED, F_USED, F_USED, F_USED,

  (BYTE) F_END
};


extern WORD MIXER_TASK_NUMBER;

#ifdef VXD_MODEL
#pragma VxD_LOCKED_CODE_SEG
#endif

#if defined( DOS_MODEL ) || defined( NT_MODEL )
/*-------------------------------------------------------------------------- */
/* */
/*  PBYTE _HeapAllocate */
/* */
/*  Description: */
/*      Allocate memory block of specified length. */
/* */
/*  Parameters: */
/*      DWORD dwLength */
/*         Length in bytes of memory block. */
/* */
/*      DWORD dwFlags */
/*         Allocation flags. */
/* */
/*  Return (PBYTE): */
/*      Pointer to allocated memory block. */
/* */
/*-------------------------------------------------------------------------- */

/*ARGSUSED*/
PBYTE
_HeapAllocate (DWORD dwLength, DWORD dwFlags)
{
#ifdef DOS_MODEL
  return (PBYTE) malloc (dwLength);
#endif

#ifdef NT_MODEL
  return (PBYTE) KERNEL_MALLOC (dwLength);
#endif

}				/* _HeapAllocate() */

/*-------------------------------------------------------------------------- */
/* */
/*  DWORD _HeapFree */
/* */
/*  Description: */
/*      Deallocate previously allocated memory block. */
/* */
/*  Parameters: */
/*      PVOID pvBlock, */
/*         Pointer to previously allocated memory block. */
/* */
/*      DWORD dwFlags */
/*         Allocation flags. */
/* */
/*  Return (DWORD): */
/*      Non-zero if successful, zero otherwise. */
/* */
/*-------------------------------------------------------------------------- */

/*ARGSUSED*/
DWORD
_HeapFree (PVOID pvBlock, DWORD dwFlags)
{
#ifdef DOS_MODEL
  free (pvBlock);
#endif

#ifdef NT_MODEL
  KERNEL_FREE (pvBlock);
#endif

  return 1;

}				/* _HeapFree() */
#endif

/*-------------------------------------------------------------------------- */
/* */
/*  VOID kDelayNMicroseconds */
/* */
/*  Description: */
/*      Delay specified number of microseconds. */
/* */
/*  Parameters: */
/*      DWORD dwCount */
/*         Number of microseconds to delay. */
/* */
/*  Return (VOID): */
/* */
/*-------------------------------------------------------------------------- */

VOID
kDelayNMicroseconds (DWORD dwCount)
{
  oss_udelay (dwCount);
}				/* kDelayNMicroseconds() */

/*-------------------------------------------------------------------------- */
/* */
/*  BYTE kInB */
/* */
/*  Description: */
/*      Do BYTE I/O read. */
/* */
/*  Parameters: */
/*      DWORD dwPort */
/*         I/O port address. */
/* */
/*  Return (BYTE): */
/*      Data read from specified address. */
/* */
/*-------------------------------------------------------------------------- */

/*ARGSUSED*/
BYTE
kInB (allegro_devc * devc, DWORD dwPort)
{
  BYTE bData;
  bData = READ_PORT_UCHAR (devc->osdev, dwPort);
  return bData;
}				/* kInB() */

/*-------------------------------------------------------------------------- */
/* */
/*  VOID kOutB */
/* */
/*  Description: */
/*      Do BYTE I/O write. */
/* */
/*  Parameters: */
/*      DWORD dwPort */
/*         I/O port address. */
/* */
/*      BYTE bData */
/*         I/O port data. */
/* */
/*  Return (VOID): */
/* */
/*-------------------------------------------------------------------------- */

/*ARGSUSED*/
VOID
kOutB (allegro_devc * devc, DWORD dwPort, BYTE bData)
{
  WRITE_PORT_UCHAR (devc->osdev, dwPort, bData);
}				/* kOutB() */

/*-------------------------------------------------------------------------- */
/* */
/*  WORD kInW */
/* */
/*  Description: */
/*      Do WORD I/O read. */
/* */
/*  Parameters: */
/*      DWORD dwPort */
/*         I/O port address. */
/* */
/*  Return (WORD): */
/*      Data read from specified address. */
/* */
/*-------------------------------------------------------------------------- */

/*ARGSUSED*/
WORD
kInW (allegro_devc * devc, DWORD dwPort)
{
  WORD wData;
  wData = READ_PORT_USHORT (devc->osdev, dwPort);
  return wData;
}				/* kInW() */

/*-------------------------------------------------------------------------- */
/* */
/*  VOID kOutW */
/* */
/*  Description: */
/*      Do WORD I/O write. */
/* */
/*  Parameters: */
/*      DWORD dwPort */
/*         I/O port address. */
/* */
/*      WORD wData */
/*         I/O port data. */
/* */
/*  Return (VOID): */
/* */
/*-------------------------------------------------------------------------- */

/*ARGSUSED*/
VOID
kOutW (allegro_devc * devc, DWORD dwPort, WORD wData)
{
  WRITE_PORT_USHORT (devc->osdev, dwPort, wData);
}				/* kOutW() */

/*-------------------------------------------------------------------------- */
/* */
/*  VOID kInsW */
/* */
/*  Description: */
/*      Do WORD I/O repeat read. */
/* */
/*  Parameters: */
/*      DWORD dwPort */
/*         I/O port address. */
/* */
/*      DWORD dwLen */
/*         Number of WORDs to read from specified address. */
/* */
/*      DWORD dwAddr */
/*         Host memory address to write to. */
/* */
/*  Return (VOID): */
/* */
/*-------------------------------------------------------------------------- */

/*ARGSUSED*/
VOID
kInsW (allegro_devc * devc, DWORD dwPort, DWORD dwLen, DWORD dwAddr)
{
#ifdef NON_INTEL
  DWORD i;
  WORD *wptr;
#endif

  if (!dwLen)
    return;

#ifdef DOS_MODEL

#ifdef NON_INTEL
  wptr = (WORD *) dwAddr;
  for (i = 0; i < dwLen; ++i)
    {
      *wptr++ = inpw (devc, (WORD) dwPort);
    }				/* endfor */
#else
  _asm
  {
  push di
      push es
      cld
      mov cx, word ptr dwLen
      mov dx, word ptr dwPort les di, dwAddr rep insw pop es pop di}
#endif

#endif

#if defined( VXD_MODEL ) || defined( WDM_MODEL ) || defined( NT_MODEL )

#ifdef NON_INTEL
  wptr = (WORD *) dwAddr;
  for (i = 0; i < dwLen; ++i)
    {
      *wptr++ = inpw (devc->osdev, (WORD) dwPort);
    }				/* endfor */
#else
  _asm
  {
  push edi
      cld mov ecx, dwLen mov edx, dwPort mov edi, dwAddr rep insw pop edi}
#endif

#endif

}				/* kInsW() */

/*-------------------------------------------------------------------------- */
/* */
/*  VOID kOutsW */
/* */
/*  Description: */
/*      Do WORD I/O repeat write. */
/* */
/*  Parameters: */
/*      DWORD dwPort */
/*         I/O port address. */
/* */
/*      DWORD dwLen */
/*         Number of WORDs to write to specified address. */
/* */
/*      DWORD dwAddr */
/*         Host memory address to read from. */
/* */
/*  Return (VOID): */
/* */
/*-------------------------------------------------------------------------- */

/*ARGSUSED*/
VOID
kOutsW (allegro_devc * devc, DWORD dwPort, DWORD dwLen, DWORD dwAddr)
{
#ifdef NON_INTEL
  DWORD i;
  WORD *wptr;
#endif

  if (!dwLen)
    return;


#ifdef DOS_MODEL
#ifdef NON_INTEL

  wptr = (WORD *) dwAddr;
  for (i = 0; i < dwLen; ++i)
    {
      outpw (devc, (WORD) dwPort, *wptr++);
    }				/* endfor */

#else
/*#pragma message("----Using slow I/O to overcome hardware bug") */
  _asm
  {
    push si
      push ds
      cld
      mov cx, word ptr dwLen mov dx, word ptr dwPort lds si, dwAddr rep outsw;
  xx:
    ;
    in al, 80 h;
    delay !;
    in al, 80 h;
    in al, 80 h;
    in al, 80 h;
    in al, 80 h;
    in al, 80 h;
    in al, 80 h;
    in al, 80 h;
    ;
    lodsw;
    get data;
    out dx, ax;
    write it;
    loop xx;
  get going ... pop ds pop si}
#endif
#endif

#if defined( VXD_MODEL ) || defined( WDM_MODEL ) || defined( NT_MODEL )
#ifdef NON_INTEL
  wptr = (WORD *) dwAddr;
  for (i = 0; i < dwLen; ++i)
    {
      outpw (devc->osdev, (WORD) dwPort, *wptr++);
    }				/* endfor */
#else
  _asm
  {
    push esi cld mov ecx, dwLen mov edx, dwPort mov esi, dwAddr rep outsw;
      xx:;
    in al, 80 h;
      delay !;
    in al, 80 h;
    in al, 80 h;
    in al, 80 h;
    in al, 80 h;
    in al, 80 h;
    in al, 80 h;
    in al, 80 h;
      lodsw;
    get data;
    out dx, ax;
    write it;
    loop xx;
  get going ... pop esi}
#endif
#endif

}				/* kOutsW() */

/*-------------------------------------------------------------------------- */
/* */
/*  WORD kDspReadWord */
/* */
/*  Description: */
/*      Read WORD from DSP memory. */
/* */
/*  Parameters: */
/*      DWORD dwBaseIO */
/*         Base I/O address of device. */
/* */
/*      DWORD dwMemType */
/*         Type of memory to read from. */
/* */
/*      DWORD dwMemAddr */
/*         Memory address to read from. */
/* */
/*  Return (WORD): */
/*      Data read from specified address. */
/* */
/*-------------------------------------------------------------------------- */

WORD
kDspReadWord (allegro_devc * devc, DWORD dwBaseIO, DWORD dwMemType,
	      DWORD dwMemAddr)
{
  WORD wData;

  CRITENTER
    /* 10/04/97, per Henry Tsay, write zeros to bits 15-2 of type register */
    kOutW (devc, dwBaseIO + DSP_PORT_MEMORY_TYPE, (WORD) dwMemType);
  kOutW (devc, dwBaseIO + DSP_PORT_MEMORY_INDEX, (WORD) dwMemAddr);
  wData = kInW (devc, dwBaseIO + DSP_PORT_MEMORY_DATA);
  CRITLEAVE return wData;
}				/* kDspReadWord() */

/*-------------------------------------------------------------------------- */
/* */
/*  VOID kDspWriteWord */
/* */
/*  Description: */
/*      Write WORD to DSP memory. */
/* */
/*  Parameters: */
/*      DWORD dwBaseIO */
/*         Base I/O address of device. */
/* */
/*      DWORD dwMemType */
/*         Type of memory to write to. */
/* */
/*      DWORD dwMemAddr */
/*         Memory address to write to. */
/* */
/*      WORD wMemData */
/*         Data to write to specified address. */
/* */
/*  Return (VOID): */
/* */
/*-------------------------------------------------------------------------- */

VOID kDspWriteWord
  (allegro_devc * devc, DWORD dwBaseIO, DWORD dwMemType, DWORD dwMemAddr,
   WORD wMemData)
{

  CRITENTER
    /* 10/04/97, per Henry Tsay, write zeros to bits 15-2 of type register */
    kOutW (devc, dwBaseIO + DSP_PORT_MEMORY_TYPE, (WORD) dwMemType);
  kOutW (devc, dwBaseIO + DSP_PORT_MEMORY_INDEX, (WORD) dwMemAddr);
  kOutW (devc, dwBaseIO + DSP_PORT_MEMORY_DATA, wMemData);
CRITLEAVE}			/* kDspWriteWord() */

/*----------------------------------------------------------------------*/
/* 									*/
/*  VOID kDspReadWords 							*/
/* 									*/
/*  Description: 							*/
/*      Read WORD block from DSP memory. 				*/
/* 									*/
/*  Parameters: 							*/
/*      DWORD dwBaseIO 							*/
/*         Base I/O address of device. 					*/
/* 									*/
/*      DWORD dwMemType 						*/
/*         Type of memory to read from. 				*/
/* 									*/
/*      DWORD dwMemAddr 						*/
/*         Memory address to read from. 				*/
/* 									*/
/*      DWORD dwMemLen 							*/
/*         Number of WORDs to read from specified address. 		*/
/* 									*/
/*      PWORD pwHostAddr 						*/
/*         Host memory address to write to. 				*/
/* 									*/
/*  Return (VOID): 							*/
/* 									*/
/*----------------------------------------------------------------------*/

VOID kDspReadWords
  (allegro_devc * devc, DWORD dwBaseIO,
   DWORD dwMemType, DWORD dwMemAddr, DWORD dwMemLen, PWORD pwHostAddr)
{

  CRITENTER
    /* 10/04/97, per Henry Tsay, write zeros to bits 15-2 of type register */
    kOutW (devc, dwBaseIO + DSP_PORT_MEMORY_TYPE, (WORD) dwMemType);
  kOutW (devc, dwBaseIO + DSP_PORT_MEMORY_INDEX, (WORD) dwMemAddr);
  kInsW (devc, dwBaseIO + DSP_PORT_MEMORY_DATA, dwMemLen, (DWORD) pwHostAddr);
CRITLEAVE}			/* kDspReadWords() */


/*-------------------------------------------------------------------------- */
/* */
/*  VOID kDspReadLongWords */
/* */
/*  Description: */
/*      Read WORD block which could be > 0x1000 from DSP memory. */
/*      Since auto-increment mode can cross 4K boundary, 4K block at a time */
/* */
/*  Parameters: */
/*      DWORD dwBaseIO */
/*         Base I/O address of device. */
/* */
/*      DWORD dwMemType */
/*         Type of memory to read from. */
/* */
/*      DWORD dwMemAddr */
/*         Memory address to read from. */
/* */
/*      DWORD dwMemLen */
/*         Number of WORDs to read from specified address. */
/* */
/*      PWORD pwHostAddr */
/*         Host memory address to write to. */
/* */
/*  Return (VOID): */
/* */
/*-------------------------------------------------------------------------- */

VOID kDspReadLongWords
  (allegro_devc * devc, DWORD dwBaseIO,
   DWORD dwMemType, DWORD dwMemLongAddr, DWORD dwMemLongLen, PWORD pwHostAddr)
{
  DWORD dwMemLen;
  DWORD dwMemBegin;
  WORD *pwBuffer;

  /* take care the Data area which could be > 0x1000 in size */
  dwMemLen = dwMemLongLen;
  dwMemBegin = dwMemLongAddr;
  pwBuffer = pwHostAddr;

  while (dwMemLen > 0)
    {
      if (dwMemLen >= 0x1000)
	{
	  kDspReadWords (devc, dwBaseIO, dwMemType, dwMemBegin, 0x1000,
			 pwBuffer);

	  dwMemBegin += 0x1000;
	  dwMemLen -= 0x1000;
	  pwBuffer = (pwBuffer + 0x1000);

	}
      else
	{
	  kDspReadWords (devc, dwBaseIO, dwMemType, dwMemBegin, dwMemLen,
			 pwBuffer);

	  dwMemLen = 0;

	}			/* endif */
    }				/* endwhile */
}				/* kDspReadLongWords() */


/*-------------------------------------------------------------------------- */
/* */
/*  VOID kDspWriteWords */
/* */
/*  Description: */
/*      Write WORD block to DSP memory. */
/* */
/*  Parameters: */
/*      DWORD dwBaseIO */
/*         Base I/O address of device. */
/* */
/*      DWORD dwMemType */
/*         Type of memory to write to. */
/* */
/*      DWORD dwMemAddr */
/*         Memory address to write to. */
/* */
/*      DWORD dwMemLen */
/*         Number of WORDs to write to specified address. */
/* */
/*      PWORD pwHostAddr */
/*         Host memory address to read from. */
/* */
/*  Return (VOID): */
/* */
/*-------------------------------------------------------------------------- */

VOID kDspWriteWords
  (allegro_devc * devc, DWORD dwBaseIO,
   DWORD dwMemType, DWORD dwMemAddr, DWORD dwMemLen, PWORD pwHostAddr)
{

  CRITENTER
    /* 10/04/97, per Henry Tsay, write zeros to bits 15-2 of type register */
    kOutW (devc, dwBaseIO + DSP_PORT_MEMORY_TYPE, (WORD) dwMemType);
  kOutW (devc, dwBaseIO + DSP_PORT_MEMORY_INDEX, (WORD) dwMemAddr);
  kOutsW (devc, dwBaseIO + DSP_PORT_MEMORY_DATA, dwMemLen,
	  (DWORD) pwHostAddr);
CRITLEAVE}			/* kDspWriteWords() */


/*-------------------------------------------------------------------------- */
/* */
/*  VOID kDspWriteLongWords */
/* */
/*  Description: */
/*      Write WORD block which could be > 0x1000 from DSP memory. */
/*      Since auto-increment mode can cross 4K boundary, 4K block at a time */
/* */
/*  Parameters: */
/*      DWORD dwBaseIO */
/*         Base I/O address of device. */
/* */
/*      DWORD dwMemType */
/*         Type of memory to Write to. */
/* */
/*      DWORD dwMemAddr */
/*         Memory address to Write to . */
/* */
/*      DWORD dwMemLen */
/*         Number of WORDs to write to specified address. */
/* */
/*      PWORD pwHostAddr */
/*         Host memory address to read from. */
/* */
/*  Return (VOID): */
/* */
/*-------------------------------------------------------------------------- */

VOID kDspWriteLongWords
  (allegro_devc * devc, DWORD dwBaseIO,
   DWORD dwMemType, DWORD dwMemLongAddr, DWORD dwMemLongLen, PWORD pwHostAddr)
{
  DWORD dwMemLen;
  DWORD dwMemBegin;
  WORD *pwBuffer;

  /* take care the Data area which could be > 0x1000 in size */
  dwMemLen = dwMemLongLen;
  dwMemBegin = dwMemLongAddr;
  pwBuffer = pwHostAddr;

  while (dwMemLen > 0)
    {
      if (dwMemLen >= 0x1000)
	{
	  kDspWriteWords (devc, dwBaseIO, dwMemType, dwMemBegin, 0x1000,
			  pwBuffer);

	  dwMemBegin += 0x1000;
	  dwMemLen -= 0x1000;
	  pwBuffer = (pwBuffer + 0x1000);

	}
      else
	{
	  kDspWriteWords (devc, dwBaseIO,
			  dwMemType, dwMemBegin, dwMemLen, pwBuffer);

	  dwMemLen = 0;

	}			/* endif */
    }				/* endwhile */
}				/* kDspWriteLongWords() */


/*-------------------------------------------------------------------------- */
/* */
/*  VOID kDspWriteZeros */
/* */
/*  Description: */
/*      Write zeros to DSP memory. */
/* */
/*  Parameters: */
/*      DWORD dwBaseIO */
/*         Base I/O address of device. */
/* */
/*      DWORD dwMemType */
/*         Type of memory to write to. */
/* */
/*      DWORD dwMemAddr */
/*         Memory address to write to. */
/* */
/*      DWORD dwMemLen */
/*         Number of WORDs of zero to write to specified address. */
/* */
/*  Return (VOID): */
/* */
/*-------------------------------------------------------------------------- */

VOID kDspWriteZeros
  (allegro_devc * devc, DWORD dwBaseIO, DWORD dwMemType, DWORD dwMemAddr,
   DWORD dwMemLen)
{
  while (dwMemLen--)
    {
/*#pragma message("----Using slow I/O to overcome hardware bug") */
/*         kDelayNMicroseconds( 8 ) ; */
      kDspWriteWord (devc, dwBaseIO, dwMemType, dwMemAddr++, 0);
    }

}				/* kDspWriteZeros() */


/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kQueryPosition */
/* */
/*  Description: */
/*      Return a client's current stream position.  A position equal to 0 */
/*      indicates the first byte in the buffer is being transferred.  A */
/*      position equal to dwHostXXXBufferLen-1 indicates the last byte */
/*      in the buffer is being transferred. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      PCLIENT_INST pClient_Inst */
/*         Pointer to client instance */
/* */
/*      DWORD dwQueryOutput */
/*         TRUE if querying host client output position, FALSE if querying input */
/* */
/*      PDWORD pdwPosition */
/*         Pointer to DWORD that will contain the byte position */
/* */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */

KRETURN kQueryPosition
  (allegro_devc * devc, PHWI phwi,
   PCLIENT_INST pClient_Inst, DWORD dwQueryOutput, PDWORD pdwPosition)
{
  DWORD dwClient = pClient_Inst->dwClient;
  WORD wPosition = 0;
  WORD wRetry = 10;
  WORD wPositionL = 0;
  WORD wPositionH = 0;
  PWORD pwCur;

  /* */
  /* Fail if the DSP kernel has been unloaded */
  /* */

  if (phwi->dwFlags & HWI_FLAG_UNLOADED)
    return KRETURN_ERROR_UNLOADED;

  /* */
  /* Make sure client and an instance exist... */
  /* */

  if (dwClient >= NUMBER_OF_CLIENTS)
    return KRETURN_ERROR_GENERIC;

  if (!phwi->asClientTable[dwClient].dwReferenceCount)
    return KRETURN_ERROR_GENERIC;

  if ((dwQueryOutput && pClient_Inst->dwDSPInConnection == KCONNECT_DMA) ||
      (!dwQueryOutput && pClient_Inst->dwDSPOutConnection == KCONNECT_DMA))
    {
      /* */
      /* if DMA is not active return position 0 */
      /* */

      pwCur = phwi->awVirtualDMAList;

      while ((*pwCur != F_FREE) && (*pwCur != (WORD) F_END))
	{
	  if (*pwCur ==
	      (WORD) (pClient_Inst->dwDspDataClientArea >> DP_SHIFT_COUNT))
	    {
	      break;
	    }
	  ++pwCur;
	}

      if (*pwCur !=
	  (WORD) (pClient_Inst->dwDspDataClientArea >> DP_SHIFT_COUNT))
	{
	  *pdwPosition = 0;
	  return KRETURN_SUCCESS;
	}

      /* */
      /* Get the position */
      /* */

      while (wRetry--)
	{
	  /* read high/low word of current position */

	  wPositionH = kDspReadWord (devc, phwi->dwBaseIO,
				     MEMTYPE_INTERNAL_DATA,
				     pClient_Inst->
				     dwDspDataClientArea +
				     CDATA_HOST_SRC_CURRENTH);

	  wPositionL = kDspReadWord (devc, phwi->dwBaseIO,
				     MEMTYPE_INTERNAL_DATA,
				     pClient_Inst->
				     dwDspDataClientArea +
				     CDATA_HOST_SRC_CURRENTL);

	  /* if the high word hasn't changed, we've got a meaningful */
	  /* current position value */

	  wPosition = kDspReadWord (devc, phwi->dwBaseIO,
				    MEMTYPE_INTERNAL_DATA,
				    pClient_Inst->
				    dwDspDataClientArea +
				    CDATA_HOST_SRC_CURRENTH);

	  if (wPosition == wPositionH)
	    break;
	}

      /* fail if we couldn't get a meaningful position */

      if (wPosition != wPositionH)
	return KRETURN_ERROR_GENERIC;

      *pdwPosition = MAKELONG (wPositionL, wPositionH) -
	(dwQueryOutput ?
	 pClient_Inst->dwHostSrcBufferAddr :
	 pClient_Inst->dwHostDstBufferAddr);
    }
  else if (!dwQueryOutput && pClient_Inst->dwDSPOutConnection == KCONNECT_PIO)
    {
      /* */
      /* Get the position */
      /* */

      *pdwPosition = pClient_Inst->dwHostDstCurrent -
	pClient_Inst->dwHostDstBufferAddr;
    }
  else
    {
      return KRETURN_ERROR_GENERIC;
    }

  return KRETURN_SUCCESS;

}				/* kQueryPosition() */


/*-------------------------------------------------------------------------- */
/* */
/*  BYTE kDspHalt */
/* */
/*  Description: */
/*      Halt the DSP. */
/* */
/*  Parameters: */
/*      DWORD dwBaseIO */
/*         Base I/O address of device. */
/* */
/*  Return (BYTE): */
/*      Contents of reset port. */
/* */
/*-------------------------------------------------------------------------- */

BYTE
kDspHalt (allegro_devc * devc, DWORD dwBaseIO)
{
  BYTE bData;
  bData = kInB (devc, dwBaseIO + DSP_PORT_CONTROL_REG_B) & ~REGB_STOP_CLOCK;
  /* Fix for HP Typhoon Hibernation Problem RJJ 6/17/00 */
  kDelayNMicroseconds (10);
  kOutB (devc, dwBaseIO + DSP_PORT_CONTROL_REG_B,
	 (BYTE) (bData & ~REGB_ENABLE_RESET));

  return bData;

}				/* kDspHalt() */

/*-------------------------------------------------------------------------- */
/* */
/*  VOID kDspReset */
/* */
/*  Description: */
/*      Reset the DSP and let it run. */
/* */
/*  Parameters: */
/*      DWORD dwBaseIO */
/*         Base I/O address of device. */
/* */
/*      BYTE bData */
/*         Contents of reset port. */
/* */
/*  Return (VOID): */
/* */
/*-------------------------------------------------------------------------- */

VOID
kDspReset (allegro_devc * devc, DWORD dwBaseIO, BYTE bData)
{
  kOutB (devc, dwBaseIO + DSP_PORT_CONTROL_REG_B,
	 (BYTE) (bData | REGB_ENABLE_RESET));

}				/* kDspReset() */

/*-------------------------------------------------------------------------- */
/* */
/*  VOID kDisableFMMap */
/* */
/*  Description: */
/*      Enable/disable FM address mapping. */
/* */
/*  Parameters: */
/*      DWORD dwBaseIO */
/*         Base I/O address of device. */
/* */
/*      DWORD dwDisable */
/*         TRUE if disabling FM mapping, FALSE otherwise */
/* */
/*  Return (VOID): */
/* */
/*-------------------------------------------------------------------------- */

VOID
kDisableFMMap (allegro_devc * devc, DWORD dwBaseIO, DWORD dwDisable)
{
  BYTE bData;

  bData =
    kInB (devc, dwBaseIO + DSP_PORT_CONTROL_REG_C) & ~REGC_DISABLE_FM_MAPPING;
  bData |= (dwDisable ? REGC_DISABLE_FM_MAPPING : 0);
  kOutB (devc, dwBaseIO + DSP_PORT_CONTROL_REG_C, (BYTE) bData);

}				/* kDisableFMMap() */

/*-------------------------------------------------------------------------- */
/* */
/*  VOID kRestartStreams */
/* */
/*  Description: */
/*      Restart all previously active streams. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*  Return (VOID): */
/* */
/*-------------------------------------------------------------------------- */

VOID
kRestartStreams (allegro_devc * devc, PHWI phwi)
{
  /* */
  /* reset DMA state machine in case it was active during the suspend */
  /* */
  /* If DMA was active, the worst thing that could happen is that we */
  /* will re-transfer the 16 words that were being transfered during */
  /* the suspend.  The 16 words are not duplicated in the stream, they */
  /* simply overwrite the exact copy that was previously transfered. */
  /* */

  if (phwi->awVirtualDMAList[0])
    {
      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA, KDATA_DMA_ACTIVE, FALSE);
    }


}				/* kRestartStreams() */

/*-------------------------------------------------------------------------- */
/* */
/*  DWORD kStateExists */
/* */
/*  Description: */
/*      Checks if the specified DSP data memory location is TRUE or FALSE. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      DWORD dwMemAddr */
/*         Memory address to check. */
/* */
/*      DWORD dwState */
/*         Specified state to check for (TRUE or FALSE) */
/* */
/*  Return (DWORD): */
/*      TRUE if specified state exists, FALSE otherwise */
/* */
/*-------------------------------------------------------------------------- */

#define RESET_IF_TIMEOUT

DWORD
kStateExists (allegro_devc * devc, PHWI phwi, DWORD dwMemAddr, DWORD dwState)
{
#ifdef RESET_IF_TIMEOUT
  DWORD dwResetLoops = 50;
#endif
  DWORD dwLoops;
  WORD wData;
  BYTE bData;

  for (dwLoops = 0;; ++dwLoops)
    {
      wData =
	kDspReadWord (devc, phwi->dwBaseIO, MEMTYPE_INTERNAL_DATA, dwMemAddr);

      if ((dwState && wData) || (!dwState && !wData))
	return TRUE;

      kDelayNMicroseconds (1);

      if (dwLoops == DSP_TIMEOUT)
	{
#ifdef RESET_IF_TIMEOUT

	  /* */
	  /* For some as yet unknown reason, if HSP/DSP modem runs */
	  /* concurrently with other DSP clients for a long time, */
	  /* the DSP stops executing.  Under such conditions a reset */
	  /* will resume DSP execution and the clients appear to run */
	  /* fine again.  Hence we do a reset if we detect the DSP */
	  /* kernel is no longer running. */
	  /* */

	  if (--dwResetLoops)
	    {
	      dwLoops = 0;
	      continue;
	    }

	  bData = kDspHalt (devc, phwi->dwBaseIO);

	  kRestartStreams (devc, phwi);

	  kDspReset (devc, phwi->dwBaseIO, bData);

	  /* */
	  /* Update our DSP reset counter.  This is purely for */
	  /* diagnostic purposes. */
	  /* */

	  if (phwi->wDspResetCount + 1)
	    ++phwi->wDspResetCount;

	  kDspWriteWord (devc, phwi->dwBaseIO,
			 MEMTYPE_INTERNAL_DATA,
			 KDATA_DSP_RESET_COUNT, phwi->wDspResetCount);
#endif

	  return FALSE;
	}
    }

}				/* kStateExists() */

/*-------------------------------------------------------------------------- */
/* */
/*  PBYTE kAllocDspMemory */
/* */
/*  Description: */
/*      Find and allocate available DSP memory. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      DWORD dwClient */
/*         Client ID */
/* */
/*      PBYTE pbMemoryMap */
/*         Pointer to memory map. */
/* */
/*      DWORD dwNumWanted */
/*         Number of contiguous memory units requested. */
/* */
/*  Return (PBYTE): */
/*      Pointer to starting memory map unit if successful, NULL otherwise */
/* */
/*-------------------------------------------------------------------------- */

PBYTE kAllocDspMemory
  (PHWI phwi, DWORD dwClient, PBYTE pbMemoryMap, DWORD dwNumWanted)
{
  PBYTE pbMemFound = NULL;
  PBYTE pbMemFound2 = NULL;
  DWORD dwNumFound = (DWORD) - 1;	/* maximum 0xFFFF */
  PBYTE pbMemTmp;
  DWORD dwNumTmp;
  PBYTE pbMemType;


  if (!dwNumWanted)
    return NULL;

  pbMemType = pbMemoryMap;	/*AY990524 Data & Code type */

  /* */
  /* Search for available memory.  We want to find the smallest */
  /* chunk of memory that can satisfy the request.  This will minimize */
  /* memory fragmentation. */
  /* */

  for (; *pbMemoryMap != (BYTE) F_END; ++pbMemoryMap)
    {
      if (*pbMemoryMap == F_USED)
	continue;

      pbMemTmp = pbMemoryMap;
      dwNumTmp = 1;

      while (*++pbMemoryMap == F_FREE)
	++dwNumTmp;

      if ((dwNumTmp >= dwNumWanted) && (dwNumTmp < dwNumFound))
	{
	  pbMemFound = pbMemTmp;
	  dwNumFound = dwNumTmp;
	  /*AY+ */
	  /* */
	  /* memory found, check whether it matches with client Code address */
	  /* */
	  if (pbMemType == (phwi->abCodeMemoryMap))	/* code memory */
	    {
	      DWORD dwCodeArea;
	      /*LINTED*/
	      dwCodeArea = ((pbMemFound - phwi->abCodeMemoryMap) *
			    phwi->dwCodeMemoryUnitLength) +
		phwi->dwCodeMemoryBegin;

	      if (!kBinStructAddress (phwi, dwClient, dwCodeArea))
		{
		  /* no code binary found */
		  pbMemoryMap = pbMemTmp;	/* continue with next search */
		  ++pbMemoryMap;
		  dwCodeArea = NULL;
		  pbMemFound = NULL;
		  dwNumFound = (DWORD) - 1;

		}
	      else
		{

		  /* */
		  /* At least we found something availble, so remember just in case. */
		  /* */

		  pbMemFound2 = pbMemFound;

		}

	    }
	  /*AY- */
	}
      --pbMemoryMap;
    }

  /* */
  /* If memory found, allocate it.  Only allocate as many units that are */
  /* necessary to satisfy the request.   If the smallest memory is not  */
  /* found, then check whether other memory is available or not because  */
  /* just failing is the last thing that kernel would want to do. */
  /* */

  if (pbMemFound)
    {

      pbMemTmp = pbMemFound;
      dwNumTmp = dwNumWanted;

      while (dwNumTmp--)
	*pbMemTmp++ = F_USED;

    }
  else
    {
      if (pbMemFound2)
	{

	  pbMemTmp = pbMemFound2;
	  dwNumTmp = dwNumWanted;
	  pbMemFound = pbMemFound2;

	  while (dwNumTmp--)
	    *pbMemTmp++ = F_USED;

	}

    }

  return pbMemFound;

}				/* kAllocDspMemory() */

/*-------------------------------------------------------------------------- */
/* */
/*  DWORD kDeallocDspMemory */
/* */
/*  Description: */
/*      Deallocate DSP memory. */
/* */
/*  Parameters: */
/*      PBYTE pbMemFree */
/*         Pointer to starting memory map unit to free. */
/* */
/*      DWORD dwNumFree */
/*         Number of contiguous memory units to free. */
/* */
/*  Return (DWORD): */
/*      TRUE if successful, FALSE otherwise */
/* */
/*-------------------------------------------------------------------------- */

DWORD
kDeallocDspMemory (PBYTE pbMemFree, DWORD dwNumFree)
{

  while (dwNumFree--)
    *pbMemFree++ = F_FREE;

  return TRUE;

}				/* kDeallocDspMemory() */

/*-------------------------------------------------------------------------- */
/* */
/*  DWORD kConnectDirectMixer */
/* */
/*  Description: */
/*      Connect/Disconnect the specified directly mixed input and output. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      DWORD dwDSPInConnection */
/*         KCONNECT_XXX indicating input connection */
/* */
/*      DWORD dwDSPOutConnection */
/*         KCONNECT_XXX indicating output connection */
/* */
/*      DWORD dwConnect */
/*         TRUE if connecting, FALSE otherwise */
/* */
/*  Return (DWORD): */
/*      TRUE if successful, FALSE otherwise */
/* */
/*-------------------------------------------------------------------------- */

DWORD kConnectDirectMixer
  (allegro_devc * devc, PHWI phwi,
   DWORD dwDSPInConnection, DWORD dwDSPOutConnection, DWORD dwConnect)
{
  WORD wAddress = 0;
  WORD wData;

  /* */
  /* Get input setting */
  /* */

  if (dwDSPInConnection == KCONNECT_ADC1)
    wData = DIRECTMIXER_ADC1;
  else if (dwDSPInConnection == KCONNECT_ADC2)
    wData = DIRECTMIXER_ADC2;
  else
    wData = 0;

  /* */
  /* Get output setting */
  /* */

  if ((dwDSPOutConnection == KCONNECT_SPDIF) && dwConnect)
    {
      /* set the DSP SPDIF request flag */

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA, KDATA_SPDIF_REQUEST, TRUE);
      wAddress = KDATA_SPDIF_XFER;
    }

  /* */
  /* Write setting */
  /* */

  if (dwConnect)
    {
      wData |=
	kDspReadWord (devc, phwi->dwBaseIO, MEMTYPE_INTERNAL_DATA, wAddress);
    }
  else
    {
      wData ^=
	kDspReadWord (devc, phwi->dwBaseIO, MEMTYPE_INTERNAL_DATA, wAddress);
    }

  kDspWriteWord (devc, phwi->dwBaseIO, MEMTYPE_INTERNAL_DATA, wAddress,
		 wData);

#if 0
#if (NUMBER_OF_CONNECTIONS != 0x10)
#error Assumption about number of connections failed.
#endif
#endif

  return TRUE;

}				/* kConnectDirectMixer() */

/*-------------------------------------------------------------------------- */
/* */
/*  DWORD kAddListEntry */
/* */
/*  Description: */
/*      Add entry to host and DSP resource lists. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      PWORD pwHostListBegin */
/*         Start address of host resource list. */
/* */
/*      WORD wDSPListBegin */
/*         Start address of DSP resource list. */
/* */
/*      WORD wEntryValue */
/*         Value of entry to add.  Must not equal F_FREE or F_END! */
/* */
/*  Return (DWORD): */
/*      TRUE if successful, FALSE otherwise */
/* */
/*-------------------------------------------------------------------------- */

DWORD kAddListEntry
  (allegro_devc * devc, PHWI phwi, PWORD pwHostListBegin, WORD wDSPListBegin,
   WORD wEntryValue)
{
  PWORD pwCur = pwHostListBegin;

  /* */
  /* Search for an unused list entry */
  /* */

  while (*pwCur != (WORD) F_END)
    {
      if (*pwCur == F_FREE)
	{
	  /* */
	  /* Write wEntryValue to both the host list and the DSP list */
	  /* */

	  *pwCur = wEntryValue;

	  kDspWriteWord (devc, phwi->dwBaseIO,
			 MEMTYPE_INTERNAL_DATA,
			 wDSPListBegin + (pwCur - pwHostListBegin),
			 wEntryValue);
	  return TRUE;
	}
      ++pwCur;
    }

  return FALSE;

}				/* kAddListEntry() */

/*-------------------------------------------------------------------------- */
/* */
/*  DWORD kRemoveListEntry */
/* */
/*  Description: */
/*      Remove entry from host and DSP resource lists.  Then compact */
/*      the lists if necessary. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      PWORD pwHostListBegin */
/*         Start address of host resource list. */
/* */
/*      WORD wDSPListBegin */
/*         Start address of DSP resource list. */
/* */
/*      WORD wEntryValue */
/*         Value of entry to remove.  Must not equal F_FREE or F_END! */
/* */
/*  Return (DWORD): */
/*      TRUE if successful, FALSE otherwise */
/* */
/*-------------------------------------------------------------------------- */

DWORD kRemoveListEntry
  (allegro_devc * devc, PHWI phwi, PWORD pwHostListBegin, WORD wDSPListBegin,
   WORD wEntryValue)
{
  PWORD pwCur = pwHostListBegin;
  PWORD pwRemove = NULL;

  /* */
  /* Search for the entry containing wEntryValue and for the */
  /* last data entry */
  /* */

  while ((*pwCur != F_FREE) && (*pwCur != (WORD) F_END))
    {
      if (*pwCur == wEntryValue)
	{
	  pwRemove = pwCur;
	}
      ++pwCur;
    }

  /* Point to last data entry */

  if (pwCur != pwHostListBegin)
    {
      --pwCur;
    }

  /* */
  /* Fail if an entry containing wEntryValue could not be found */
  /* */

  if (!pwRemove)
    return FALSE;

  /* */
  /* OK, we've found our entry to remove and we've got the location */
  /* of the last data entry.  When we remove the entry, the void */
  /* that is created is filled by moving the last data entry into */
  /* the void.  The last data entry is then deleted.  We have to fill */
  /* up the void because list entries are defined to be packed together */
  /* (i.e. no gaps between entries). */
  /* */

  /* */
  /* If the entry to remove is NOT the same as the last data entry, we */
  /* first overwrite the entry to be removed with the value of the last */
  /* data entry.  Then we delete the last data entry. */
  /* */
  /* If the entry to remove is the same as the last data entry, we */
  /* simply delete the last data entry. */
  /* */

  if (pwRemove != pwCur)
    {
      *pwRemove = *pwCur;
      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     wDSPListBegin + (pwRemove - pwHostListBegin), *pwCur);
    }

  /* */
  /* Delete the last data entry */
  /* */

  *pwCur = NULL;
  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA,
		 wDSPListBegin + (pwCur - pwHostListBegin), 0);

  return TRUE;

}				/* kRemoveListEntry() */

/*-------------------------------------------------------------------------- */
/* */
/*  PWORD kInstanceListAddress */
/* */
/*  Description: */
/*      Return pointer to requested client instance list. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      DWORD dwClient */
/*         Client ID */
/* */
/*  Return (PWORD): */
/*      Pointer to client instance list. */
/* */
/*-------------------------------------------------------------------------- */

PWORD
kInstanceListAddress (PHWI phwi, DWORD dwClient)
{
  switch (dwClient)
    {
    case CLIENT_CPYTHRU:
      return phwi->awInstanceCpyThruList;

    case CLIENT_MODEM:
      return phwi->awInstanceModemList;

    case CLIENT_POS3D:
      return phwi->awInstancePos3DList;

    case CLIENT_SPKVIRT:
    case CLIENT_SPKVIRT_CRL:
      return phwi->awInstanceSpkVirtList;

    case CLIENT_SRC:
      return phwi->awInstanceSRCList;

    case CLIENT_MINISRC:
      return phwi->awInstanceMINISRCList;

    case CLIENT_SPDIF:
      return phwi->awInstanceSPDIFList;


      /* we should never end up here! */
    default:
      return NULL;
    }
#if 0
#if (NUMBER_OF_CLIENTS != 8)
#error Assumption about number of clients failed.
#endif
#endif
}				/* kInstanceListAddress() */

/*-------------------------------------------------------------------------- */
/* */
/*  PWORD kHostXferListAddress */
/* */
/*  Description: */
/*      Return pointer to requested host transfer list. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      DWORD dwDSPConnection */
/*         KCONNECT_XXX indicating connection */
/* */
/*  Return (PWORD): */
/*      Pointer to host transfer list. */
/* */
/*-------------------------------------------------------------------------- */

PWORD
kHostXferListAddress (PHWI phwi, DWORD dwDSPConnection)
{
  switch (dwDSPConnection)
    {
    case KCONNECT_NONE:
      return NULL;

    case KCONNECT_DMA:
      return phwi->awVirtualDMAList;

    case KCONNECT_ADC1:
      return phwi->awVirtualADC1List;

    case KCONNECT_ADC2:
      return phwi->awVirtualADC2List;

    case KCONNECT_CD:
      return phwi->awVirtualCDList;

    case KCONNECT_MIC:
      return phwi->awVirtualMICList;

    case KCONNECT_I2S:
      return phwi->awVirtualI2SList;

    case KCONNECT_CHI:
      return phwi->awVirtualCHIList;

    case KCONNECT_SPDIFIN:
      return phwi->awVirtualSPDIFINList;

    case KCONNECT_MIXER:
      return phwi->awVirtualMIXERList;

      /*AY */
    case KCONNECT_FMIXER:
      return phwi->awVirtualFMIXERList;

    case KCONNECT_RMIXER:
      return phwi->awVirtualRMIXERList;

    case KCONNECT_SAME:
      return NULL;

      /* we should never end up here! */
    default:
      return NULL;
    }
#if 0
#if (NUMBER_OF_CONNECTIONS != 0x10)
#error Assumption about number of connections failed.
#endif
#endif
}				/* kHostXferListAddress() */

/*-------------------------------------------------------------------------- */
/* */
/*  WORD kDSPXferListAddress */
/* */
/*  Description: */
/*      Return pointer to requested DSP transfer list. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      DWORD dwDSPConnection */
/*         KCONNECT_XXX indicating connection */
/* */
/*  Return (WORD): */
/*      Pointer to DSP transfer list. */
/* */
/*-------------------------------------------------------------------------- */

/*ARGSUSED*/
WORD
kDSPXferListAddress (PHWI phwi, DWORD dwDSPConnection)
{
  switch (dwDSPConnection)
    {
    case KCONNECT_NONE:
      return NULL;

    case KCONNECT_DMA:
      return KDATA_DMA_XFER0;

    case KCONNECT_ADC1:
      return KDATA_ADC1_XFER0;

    case KCONNECT_ADC2:
      return KDATA_ADC2_XFER0;

    case KCONNECT_CD:
      return KDATA_CD_XFER0;

    case KCONNECT_MIC:
      return KDATA_MIC_XFER0;

    case KCONNECT_I2S:
      return KDATA_I2S_XFER0;

    case KCONNECT_CHI:
      return KDATA_CHI_XFER0;

    case KCONNECT_SPDIF:
      return KDATA_SPDIF_XFER;

    case KCONNECT_SPDIFIN:
      return KDATA_SPDIFIN_XFER0;

    case KCONNECT_MIXER:
      return KDATA_MIXER_XFER0;

      /*AY */
    case KCONNECT_FMIXER:
      return KDATA_FMIXER_XFER0;

    case KCONNECT_RMIXER:
      return KDATA_RMIXER_XFER0;

    case KCONNECT_SAME:
      return NULL;

      /* we should never end up here! */
    default:
      return NULL;
    }
#if 0
#if (NUMBER_OF_CONNECTIONS != 0x10)
#error Assumption about number of connections failed.
#endif
#endif
}				/* kDSPXferListAddress() */

/*-------------------------------------------------------------------------- */
/* */
/*  DWORD kConnectInputOutput */
/* */
/*  Description: */
/*      Connect the specified input and output. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      DWORD dwDSPInConnection */
/*         KCONNECT_XXX indicating input connection */
/* */
/*      DWORD dwDSPOutConnection */
/*         KCONNECT_XXX indicating output connection */
/* */
/*      DWORD dwDspDataArea */
/*         Address of DSP data area containing connection header */
/* */
/*  Return (DWORD): */
/*      TRUE if successful, FALSE otherwise */
/* */
/*-------------------------------------------------------------------------- */

DWORD kConnectInputOutput
  (allegro_devc * devc, PHWI phwi,
   DWORD dwDSPInConnection, DWORD dwDSPOutConnection, DWORD dwDspDataArea)
{
  /* */
  /* connect the input */
  /* */

  if ((dwDSPInConnection != KCONNECT_NONE) &&
      (dwDSPInConnection != KCONNECT_SAME) &&
      (dwDSPInConnection < NUMBER_OF_CONNECTIONS))
    {
      if (!kAddListEntry (devc, phwi,
			  kHostXferListAddress (phwi, dwDSPInConnection),
			  kDSPXferListAddress (phwi, dwDSPInConnection),
			  (WORD) (dwDspDataArea >> DP_SHIFT_COUNT)))
	return FALSE;

      if (dwDSPInConnection == KCONNECT_ADC1)
	kDspWriteWord (devc, phwi->dwBaseIO,
		       MEMTYPE_INTERNAL_DATA, KDATA_ADC1_REQUEST, TRUE);


      if (dwDSPInConnection == KCONNECT_ADC2)
	kDspWriteWord (devc, phwi->dwBaseIO,
		       MEMTYPE_INTERNAL_DATA, KDATA_ADC2_REQUEST, TRUE);


      if (dwDSPInConnection == KCONNECT_CD)
	kDspWriteWord (devc, phwi->dwBaseIO,
		       MEMTYPE_INTERNAL_DATA, KDATA_CD_REQUEST, TRUE);

      if (dwDSPInConnection == KCONNECT_MIC)
	kDspWriteWord (devc, phwi->dwBaseIO,
		       MEMTYPE_INTERNAL_DATA, KDATA_MIC_REQUEST, TRUE);

      if (dwDSPInConnection == KCONNECT_I2S)
	kDspWriteWord (devc, phwi->dwBaseIO,
		       MEMTYPE_INTERNAL_DATA, KDATA_I2S_INT_METER, 0);

      if (dwDSPInConnection == KCONNECT_SPDIFIN)
	{
	  kDspWriteWord (devc, phwi->dwBaseIO,
			 MEMTYPE_INTERNAL_DATA, KDATA_SPDIFIN_INT_METER, 0);

	}

    }
  else
    {
      /* */
      /*   input buffer is connected to other instance's output buffer */
      /* */

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     (WORD) (dwDspDataArea) + CDATA_IN_BUF_CONNECT,
		     (WORD) dwDSPInConnection);
    }


  /* */
  /* connect the output */
  /* */

  if ((dwDSPOutConnection != KCONNECT_NONE) &&
      (dwDSPOutConnection != KCONNECT_SAME) &&
      (dwDSPOutConnection < NUMBER_OF_CONNECTIONS))
    {
      if (!kAddListEntry (devc, phwi,
			  kHostXferListAddress (phwi, dwDSPOutConnection),
			  kDSPXferListAddress (phwi, dwDSPOutConnection),
			  (WORD) (dwDspDataArea >> DP_SHIFT_COUNT)))
	return FALSE;

      /* */
      /* if mixer, tell the dsp the total number of mixing list */
      /* */

      if (dwDSPOutConnection == KCONNECT_MIXER)
	{

	  MIXER_TASK_NUMBER++;

	  kDspWriteWord (devc, phwi->dwBaseIO,
			 MEMTYPE_INTERNAL_DATA,
			 KDATA_MIXER_TASK_NUMBER, MIXER_TASK_NUMBER);
	}
    }
  else
    {
      /* */
      /* output buffer is connected to other instance's input buffer */
      /* */

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     (WORD) (dwDspDataArea) + CDATA_OUT_BUF_CONNECT,
		     (WORD) dwDSPOutConnection);
    }

  return TRUE;

}				/* kConnectInputOutput() */

/*-------------------------------------------------------------------------- */
/* */
/*  DWORD kDisconnectInputOutput */
/* */
/*  Description: */
/*      Disconnect the specified input and output. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      DWORD dwDSPInConnection */
/*         KCONNECT_XXX indicating input connection */
/* */
/*      DWORD dwDSPOutConnection */
/*         KCONNECT_XXX indicating output connection */
/* */
/*      DWORD dwDspDataArea */
/*         Address of DSP data area containing connection header */
/* */
/*  Return (DWORD): */
/*      TRUE if successful, FALSE otherwise */
/* */
/*-------------------------------------------------------------------------- */

DWORD kDisconnectInputOutput
  (allegro_devc * devc, PHWI phwi,
   DWORD dwDSPInConnection, DWORD dwDSPOutConnection, DWORD dwDspDataArea)
{
  WORD wdata;

  /* */
  /* disconnect the input */
  /* */

  if ((dwDSPInConnection != KCONNECT_NONE) &&
      (dwDSPInConnection != KCONNECT_SAME))
    {

      kRemoveListEntry (devc, phwi,
			kHostXferListAddress (phwi, dwDSPInConnection),
			kDSPXferListAddress (phwi, dwDSPInConnection),
			(WORD) (dwDspDataArea >> DP_SHIFT_COUNT));

      if (dwDSPInConnection == KCONNECT_ADC1)
	kDspWriteWord (devc, phwi->dwBaseIO,
		       MEMTYPE_INTERNAL_DATA, KDATA_ADC1_REQUEST, FALSE);

      if (dwDSPInConnection == KCONNECT_ADC2)
	kDspWriteWord (devc, phwi->dwBaseIO,
		       MEMTYPE_INTERNAL_DATA, KDATA_ADC2_REQUEST, FALSE);

      if (dwDSPInConnection == KCONNECT_CD)
	kDspWriteWord (devc, phwi->dwBaseIO,
		       MEMTYPE_INTERNAL_DATA, KDATA_CD_REQUEST, FALSE);

      if (dwDSPInConnection == KCONNECT_MIC)
	kDspWriteWord (devc, phwi->dwBaseIO,
		       MEMTYPE_INTERNAL_DATA, KDATA_MIC_REQUEST, FALSE);

      if (dwDSPInConnection == KCONNECT_I2S)
	{
	  wdata = kDspReadWord (devc, phwi->dwBaseIO,
				MEMTYPE_INTERNAL_DATA, KDATA_I2S_ACTIVE);

	  wdata &= ~0x1;

	  kDspWriteWord (devc, phwi->dwBaseIO,
			 MEMTYPE_INTERNAL_DATA, KDATA_I2S_ACTIVE, wdata);
	}

      if (dwDSPInConnection == KCONNECT_SPDIFIN)
	{
	  wdata = kDspReadWord (devc, phwi->dwBaseIO,
				MEMTYPE_INTERNAL_DATA, KDATA_I2S_ACTIVE);

	  wdata &= ~0x2;

	  kDspWriteWord (devc, phwi->dwBaseIO,
			 MEMTYPE_INTERNAL_DATA, KDATA_I2S_ACTIVE, wdata);
	}

    }

  /* */
  /* disconnect the output */
  /* */

  if ((dwDSPOutConnection != KCONNECT_NONE) &&
      (dwDSPOutConnection != KCONNECT_SAME))
    {
      kRemoveListEntry (devc, phwi,
			kHostXferListAddress (phwi, dwDSPOutConnection),
			kDSPXferListAddress (phwi, dwDSPOutConnection),
			(WORD) (dwDspDataArea >> DP_SHIFT_COUNT));
    }

  /* */
  /* if mixer, tell the dsp the total number of mixing list */
  /* */

  if (dwDSPOutConnection == KCONNECT_MIXER)
    {

      MIXER_TASK_NUMBER--;

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     KDATA_MIXER_TASK_NUMBER, MIXER_TASK_NUMBER);
    }

  /* */
  /* handle special cases */
  /* */

  if ((dwDSPInConnection == KCONNECT_DMA) ||
      (dwDSPOutConnection == KCONNECT_DMA))
    {
      /* do DMA synchronization */

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA, KDATA_DMA_SWITCH, FALSE);
    }

  return TRUE;

}				/* kDisconnectInputOutput() */

/*-------------------------------------------------------------------------- */
/* */
/*  PCLIENT_BIN kBinStructAddress */
/* */
/*  Description: */
/*      Return pointer to requested binary image structure. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      DWORD dwClient */
/*         Client ID */
/* */
/*      DWORD dwSearchKey */
/*         Search key */
/* */
/*  Return (PCLIENT_BIN): */
/*      Pointer to structure if successful, NULL otherwise */
/* */
/*-------------------------------------------------------------------------- */

PCLIENT_BIN
kBinStructAddress (PHWI phwi, DWORD dwClient, DWORD dwSearchKey)
{
  PCLIENT_BIN pClient_Bin;

  if (dwClient >= NUMBER_OF_CLIENTS)
    return NULL;

  pClient_Bin = phwi->asClientTable[dwClient].pClient_Bin;

  /* */
  /* If search key is NULL, then just return address of first struct */
  /* */

  if (!dwSearchKey)
    return pClient_Bin;

  /* */
  /* Search key is code load address */
  /* */

  while (pClient_Bin->dwCodeAddress)
    {
      if (pClient_Bin->dwCodeAddress == dwSearchKey)
	{
	  return pClient_Bin;
	}

      ++pClient_Bin;
    }

  return NULL;

}				/* kBinStructAddress() */

/*-------------------------------------------------------------------------- */
/* */
/*  DWORD kAllocDspVectors */
/* */
/*  Description: */
/*      Allocate DSP vectors required by client. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      DWORD dwClient */
/*         Client ID */
/* */
/*  Return (DWORD): */
/*      TRUE if successful, FALSE otherwise */
/* */
/*-------------------------------------------------------------------------- */

DWORD
kAllocDspVectors (allegro_devc * devc, PHWI phwi, DWORD dwClient)
{
  PCLIENT_BIN pClient_Bin;
  DWORD dwAllocate;
  DWORD dwIndex;

  /* */
  /* Check if client needs to allocate DSP vectors */
  /* */

  if (!(pClient_Bin = kBinStructAddress (phwi, dwClient,
					 phwi->asClientTable[dwClient].
					 dwDspCodeClientArea)))
    return FALSE;

  dwAllocate = FALSE;

  for (dwIndex = KCODE_VECTORS_UNIT_LENGTH;	/* vector 0 reserved */
       dwIndex < KCODE_VECTORS_LENGTH; dwIndex += KCODE_VECTORS_UNIT_LENGTH)
    {
      if (pClient_Bin->pwBinVect[dwIndex + 0] ||
	  pClient_Bin->pwBinVect[dwIndex + 1])
	{
	  dwAllocate = TRUE;

	  /* fail if the vector is already allocated */

	  if ((phwi->awVectorList[dwIndex + 0] != F_FREE) ||
	      (phwi->awVectorList[dwIndex + 1] != F_FREE))
	    return FALSE;
	}
    }

  if (!dwAllocate)
    return TRUE;

  /* */
  /* Make sure DSP kernel is ready to be halted */
  /* */

  if (!kStateExists (devc, phwi, KDATA_HALT_ACKNOWLEDGE, FALSE))
    return FALSE;

  /* */
  /* halt the DSP kernel and wait for an acknowledgement */
  /* */

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA, KDATA_HALT_SYNCH_CLIENT, TRUE);

  if (!kStateExists (devc, phwi, KDATA_HALT_ACKNOWLEDGE, TRUE))
    {
      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA, KDATA_HALT_SYNCH_CLIENT, FALSE);

      return FALSE;
    }

  /* */
  /* DSP kernel has halted, do vector allocation */
  /* */
  /* Note: We halt the DSP in order to safely modify vectors */
  /* */

  for (dwIndex = KCODE_VECTORS_UNIT_LENGTH;	/* vector 0 reserved */
       dwIndex < KCODE_VECTORS_LENGTH; dwIndex += KCODE_VECTORS_UNIT_LENGTH)
    {
      if (pClient_Bin->pwBinVect[dwIndex + 0] ||
	  pClient_Bin->pwBinVect[dwIndex + 1])
	{
	  /* save current DSP vector */

	  phwi->awVectorList[dwIndex + 0] =
	    kDspReadWord (devc, phwi->dwBaseIO, MEMTYPE_INTERNAL_CODE,
			  dwIndex + 0);

	  phwi->awVectorList[dwIndex + 1] =
	    kDspReadWord (devc, phwi->dwBaseIO, MEMTYPE_INTERNAL_CODE,
			  dwIndex + 1);

	  /* write new DSP vector */

	  kDspWriteWord (devc, phwi->dwBaseIO,
			 MEMTYPE_INTERNAL_CODE,
			 dwIndex + 0, pClient_Bin->pwBinVect[dwIndex + 0]);

	  kDspWriteWord (devc, phwi->dwBaseIO,
			 MEMTYPE_INTERNAL_CODE,
			 dwIndex + 1, pClient_Bin->pwBinVect[dwIndex + 1]);
	}
    }

  /* */
  /* all done, reset halt request flag to resume DSP kernel execution */
  /* */

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA, KDATA_HALT_SYNCH_CLIENT, FALSE);

  return TRUE;

}				/* kAllocDspVectors() */

/*-------------------------------------------------------------------------- */
/* */
/*  DWORD kDeallocDspVectors */
/* */
/*  Description: */
/*      Deallocate DSP vectors previously required by client. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      DWORD dwClient */
/*         Client ID */
/* */
/*  Return (DWORD): */
/*      TRUE if successful, FALSE otherwise */
/* */
/*-------------------------------------------------------------------------- */

DWORD
kDeallocDspVectors (allegro_devc * devc, PHWI phwi, DWORD dwClient)
{
  PCLIENT_BIN pClient_Bin;
  DWORD dwDeallocate;
  DWORD dwIndex;

  /* */
  /* Check if client needs to deallocate DSP vectors */
  /* */

  if (!(pClient_Bin = kBinStructAddress (phwi, dwClient,
					 phwi->asClientTable[dwClient].
					 dwDspCodeClientArea)))
    return FALSE;

  dwDeallocate = FALSE;

  for (dwIndex = KCODE_VECTORS_UNIT_LENGTH;	/* vector 0 reserved */
       dwIndex < KCODE_VECTORS_LENGTH; dwIndex += KCODE_VECTORS_UNIT_LENGTH)
    {
      if (pClient_Bin->pwBinVect[dwIndex + 0] ||
	  pClient_Bin->pwBinVect[dwIndex + 1])
	{
	  dwDeallocate = TRUE;

	  /* fail if the vector was not allocated */

	  if ((phwi->awVectorList[dwIndex + 0] == F_FREE) &&
	      (phwi->awVectorList[dwIndex + 1] == F_FREE))
	    return FALSE;
	}
    }

  if (!dwDeallocate)
    return TRUE;

  /* */
  /* Make sure DSP kernel is ready to be halted */
  /* */

  if (!kStateExists (devc, phwi, KDATA_HALT_ACKNOWLEDGE, FALSE))
    return FALSE;

  /* */
  /* halt the DSP kernel and wait for an acknowledgement */
  /* */

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA, KDATA_HALT_SYNCH_CLIENT, TRUE);

  if (!kStateExists (devc, phwi, KDATA_HALT_ACKNOWLEDGE, TRUE))
    {
      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA, KDATA_HALT_SYNCH_CLIENT, FALSE);

      return FALSE;
    }

  /* */
  /* DSP kernel has halted, do vector deallocation */
  /* */
  /* Note: We halt the DSP in order to safely modify vectors */
  /* */

  for (dwIndex = KCODE_VECTORS_UNIT_LENGTH;	/* vector 0 reserved */
       dwIndex < KCODE_VECTORS_LENGTH; dwIndex += KCODE_VECTORS_UNIT_LENGTH)
    {
      if (pClient_Bin->pwBinVect[dwIndex + 0] ||
	  pClient_Bin->pwBinVect[dwIndex + 1])
	{
	  /* restore original DSP vector */

	  kDspWriteWord (devc, phwi->dwBaseIO,
			 MEMTYPE_INTERNAL_CODE,
			 dwIndex + 0, phwi->awVectorList[dwIndex + 0]);

	  kDspWriteWord (devc, phwi->dwBaseIO,
			 MEMTYPE_INTERNAL_CODE,
			 dwIndex + 1, phwi->awVectorList[dwIndex + 1]);

	  /* reset vector list */

	  phwi->awVectorList[dwIndex + 0] = F_FREE;
	  phwi->awVectorList[dwIndex + 1] = F_FREE;
	}
    }

  /* */
  /* all done, reset halt request flag to resume DSP kernel execution */
  /* */

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA, KDATA_HALT_SYNCH_CLIENT, FALSE);

  return TRUE;

}				/* kDeallocDspVectors() */

/*-------------------------------------------------------------------------- */
/* */
/*  VOID kLoadKernel */
/* */
/*  Description: */
/*      Load the kernel into DSP memory. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      DWORD dwBaseIO */
/*         Base I/O address of device. */
/* */
/*      DWORD dwFlags */
/*         Hardware instance flags. */
/* */
/*  Return (VOID): */
/* */
/*-------------------------------------------------------------------------- */

/*ARGSUSED*/
VOID
kLoadKernel (allegro_devc * devc, PHWI phwi, DWORD dwBaseIO, DWORD dwFlags)
{
  BYTE bData;

  bData = kDspHalt (devc, dwBaseIO);

  kDisableFMMap (devc, dwBaseIO, TRUE);

  /* clear DSP kernel data memory */

  kDspWriteZeros (devc, dwBaseIO,
		  MEMTYPE_INTERNAL_DATA,
		  KDATA_BASE_ADDR,
		  NUM_UNITS_KERNEL_DATA * phwi->dwDataMemoryUnitLength);

  /* clear DSP kernel mixer data memory */

  kDspWriteZeros (devc, dwBaseIO,
		  MEMTYPE_INTERNAL_DATA,
		  KDATA_BASE_ADDR2,
		  NUM_UNITS_KERNEL_DATA * phwi->dwDataMemoryUnitLength);


  /* initialize current DMA pointer */

  kDspWriteWord (devc, dwBaseIO, MEMTYPE_INTERNAL_DATA, KDATA_CURRENT_DMA, KDATA_DMA_XFER0);	/* khs 121198 */

  /* initialize SPDIF frame status pointer and array */

  kDspWriteWord (devc, dwBaseIO,
		 MEMTYPE_INTERNAL_DATA,
		 KDATA_SPDIF_CURRENT_FRAME, KDATA_SPDIF_FRAME0);

  kDspWriteWord (devc, dwBaseIO, MEMTYPE_INTERNAL_DATA, KDATA_SPDIF_FRAME0,
		 /* 0x0204 ) ; */
		 0x0100);	/*AY990508 CD data and copy prohibited for SCMS */

  kDspWriteWord (devc, dwBaseIO, MEMTYPE_INTERNAL_DATA, KDATA_SPDIF_FRAME1, 0x0200);	/*48K only */

  /* download kernel to DSP memory */
  {
    kDspWriteWords (devc, dwBaseIO,
		    MEMTYPE_INTERNAL_CODE,
		    phwi->dwCodeMemoryBegin,
		    (gsKernelVectCode.dwLengthCode + 1) / 2,
		    gsKernelVectCode.pwBinCode);
  }

  /* indicate that kernel is loaded */
  phwi->dwFlags &= ~HWI_FLAG_UNLOADED;

  /* Set Master Volume to Maximum */
  kSetMasterVolume (devc, phwi, 0x7FFF, 0x7FFF);

  kDspReset (devc, dwBaseIO, bData);

}				/* kLoadKernel() */

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kInitKernel */
/* */
/*  Description: */
/*      Initialize the host and DSP kernel. */
/* */
/*  Parameters: */
/*      PHWI *pphwi */
/*         Pointer to Pointer to hardware instance. */
/* */
/*      DWORD dwDeviceID */
/*         Device ID. */
/* */
/*      DWORD dwRevisionID */
/*         Revision ID. */
/*         should always be 0x10 for M3/Allegro1 */
/* */
/*      DWORD dwBaseIO */
/*         Base I/O address of device. */
/* */
/*      DWORD dwFlags */
/*         Hardware instance flags. */
/*         should always be 0 since there is no difference for HSP */
/* */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */

KRETURN kInitKernel
  (allegro_devc * devc, PHWI * pphwi,
   DWORD dwDeviceID, DWORD dwRevisionID, DWORD dwBaseIO, DWORD dwFlags)
{

  PHWI phwi;
  BYTE *pbSrc;
  BYTE *pbDst;
  WORD i;

#ifdef K_DBG
  _Debug_Printf_Service ("kInitKernel DID=%X RID=%X IO=%X Flag=%X\n",
			 dwDeviceID, dwRevisionID, dwBaseIO, dwFlags);
#endif
  /* */
  /* Allocate hardware instance buffer, initialize it, and pass */
  /* the buffer address back to the caller */
  /* */

  if (!(phwi = (PHWI) _HeapAllocate (sizeof (HWI), HEAPZEROINIT)))
    return KRETURN_ERROR_GENERIC;

  *phwi = ghwi;
  *pphwi = phwi;

  /* */
  /* Initialize the hardware instance */
  /* */

  phwi->dwDeviceID = dwDeviceID;
  phwi->dwRevisionID = dwRevisionID;
  phwi->dwBaseIO = dwBaseIO;
  phwi->dwFlags = dwFlags;	/* should be 0x0 */


  /* */
  /*   Check and report bad sectors in DSP Memory */
  /* */
/*aaa */
  /*if (phwi -> dwFlags & HWI_FLAG_MEM_CHECK) { */
  kDSPMemCheck (devc, phwi);
  /*      return KRETURN_ERROR_GENERIC ; */
  /* } */

  /* init memory map bounds */

  {
    phwi->dwCodeMemoryBegin = REV_B_CODE_MEMORY_BEGIN;
    phwi->dwCodeMemoryEnd = REV_B_CODE_MEMORY_END;
    phwi->dwCodeMemoryUnitLength = REV_B_CODE_MEMORY_UNIT_LENGTH;
    phwi->dwCodeMemoryLength = REV_B_CODE_MEMORY_LENGTH;

    phwi->dwDataMemoryBegin = REV_B_DATA_MEMORY_BEGIN;
    phwi->dwDataMemoryEnd = REV_B_DATA_MEMORY_END;
    phwi->dwDataMemoryUnitLength = REV_B_DATA_MEMORY_UNIT_LENGTH;
    phwi->dwDataMemoryLength = REV_B_DATA_MEMORY_LENGTH;

    /* transfer memory map images */

    pbSrc = gabRevBCodeMemoryMapImage;
    pbDst = phwi->abCodeMemoryMap;

    while ((*pbDst++ = *pbSrc++) != (BYTE) F_END);

    pbSrc = gabRevBDataMemoryMapImage;
    pbDst = phwi->abDataMemoryMap;

    while ((*pbDst++ = *pbSrc++) != (BYTE) F_END);
  }

  /* */
  /* Initialize the DSP */
  /* */

  kLoadKernel (devc, phwi, dwBaseIO, dwFlags);



  /* testing : dump memory map to 0x1800 */
  pbDst = phwi->abDataMemoryMap;

  *pbDst++ = F_USED;
  *pbDst++ = F_USED;

  pbDst = phwi->abDataMemoryMap;

  for (i = 0; i < 24; i++)
    {
      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     (DWORD) 0x10c0 + i, (WORD) ((*pbDst++) * 0xFFFF));
    }

  return KRETURN_SUCCESS;

}				/* kInitKernel() */

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kDSPMemCheck */
/* */
/*  Description: */
/*      Check the DSP Data memory and update a memory map in host kernel. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/*       */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */
/*kkk    */
KRETURN
kDSPMemCheck (allegro_devc * devc, PHWI phwi)
{
  BYTE bData;
  BYTE bData2;

  WORD wLoop = 0;
  WORD wFlag = 0;
  WORD wFlag2 = 1;
  WORD wFlag3 = 1;
  WORD i;
  DWORD addr = 0;


  /*  */
  /* begin to check memory  */
  /* */

  /* save the clock speed for restoreing */
  bData2 = kInB (devc, phwi->dwBaseIO + DSP_PORT_CONTROL_REG_A);

  for (wLoop = 0; wLoop < 20; wLoop++)
    {

      bData = kDspHalt (devc, phwi->dwBaseIO);

      /*  change to 49Mhz for intensive memory test */

      kOutB (devc, phwi->dwBaseIO + DSP_PORT_CONTROL_REG_A, (BYTE) 0x09);
/*            kOutB( phwi -> dwBaseIO + DSP_PORT_CONTROL_REG_C, (BYTE) 0x10 ) ; */


      /* clear DSP kernel data memory */

      kDspWriteZeros (devc, phwi->dwBaseIO,
		      MEMTYPE_INTERNAL_DATA,
		      KDATA_BASE_ADDR, REV_B_DATA_MEMORY_LENGTH);

      /* load memory check code */

      kDspWriteWords (devc, phwi->dwBaseIO,
		      MEMTYPE_INTERNAL_CODE,
		      phwi->dwCodeMemoryBegin,
		      (gsMemChkVectCode.dwLengthCode + 1) / 2,
		      gsMemChkVectCode.pwBinCode);

      /* start DSP */


      kDspReset (devc, phwi->dwBaseIO, bData);

      for (i = 0; i < 3; i++)
	{

	  if (i == 0)
	    addr = FLAGADD1;
	  if (i == 1)
	    addr = FLAGADD2;
	  if (i == 2)
	    addr = FLAGADD3;


	  /* initialize */

	  kDspWriteWord (devc, phwi->dwBaseIO, MEMTYPE_INTERNAL_DATA,
			 addr + 9, wFlag);

	  kDspWriteWord (devc, phwi->dwBaseIO, MEMTYPE_INTERNAL_DATA,
			 addr + 5, wFlag2);

	  kDspWriteWord (devc, phwi->dwBaseIO, MEMTYPE_INTERNAL_DATA,
			 addr + 6, wFlag3);


	  /* first 1k check */

	  /*LINTED*/
	  while (1)
	    {

	      wFlag =
		kDspReadWord (devc, phwi->dwBaseIO, MEMTYPE_INTERNAL_DATA,
			      addr);

	      if (wFlag == 0x0001)	/* passed */
		{

		  wFlag = 0;

		  kDspWriteWord (devc, phwi->dwBaseIO, MEMTYPE_INTERNAL_DATA,
				 addr, wFlag);

		  break;

		}
	      else if (wFlag == 0x0002)	/* found a bad sector  */
		{

		  wFlag =
		    kDspReadWord (devc, phwi->dwBaseIO, MEMTYPE_INTERNAL_DATA,
				  addr + 2);

		  /* update memory map */
		  gabRevBDataMemoryMapImage[((wFlag -
					      REV_B_DATA_MEMORY_BEGIN) >> 7)]
		    = F_USED;

		  wFlag = 0;

		  kDspWriteWord (devc, phwi->dwBaseIO, MEMTYPE_INTERNAL_DATA,
				 addr, wFlag);

		  kDspWriteWord (devc, phwi->dwBaseIO, MEMTYPE_INTERNAL_DATA,
				 addr + 5, wFlag2);
		}

	    }			/* while */

	}			/* for(3) */

    }				/* for(100) */


  /* */
  /* everything is done, so restore the clock */
  /* */

  bData = kDspHalt (devc, phwi->dwBaseIO);

  kOutB (devc, phwi->dwBaseIO + DSP_PORT_CONTROL_REG_A, (BYTE) bData2);

  kDspReset (devc, phwi->dwBaseIO, bData);


  return KRETURN_SUCCESS;

}

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kTermKernel */
/* */
/*  Description: */
/*      Terminate the host and DSP kernel. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      DWORD dwBaseIO */
/*         Base I/O address of device. */
/* */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */

KRETURN
kTermKernel (allegro_devc * devc, PHWI phwi, DWORD dwBaseIO)
{
  kDspHalt (devc, dwBaseIO);

  _HeapFree (phwi, 0);

  return KRETURN_SUCCESS;

}				/* kTermKernel() */

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kSuspendKernel */
/* */
/*  Description: */
/*      Suspend the kernel and save its state. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */

KRETURN
kSuspendKernel (allegro_devc * devc, PHWI phwi)
{
  /* */
  /* FM is a special case */
  /* */
#ifdef K_DBG
  _Debug_Printf_Service ("kSuspendKernel %X\n", phwi);
#endif

  if (phwi->dwFlags & HWI_FLAG_FM_LOADED)
    {
      /* */
      /* Allocate host memory */
      /* */

      if (!(phwi->pwSuspendBuffer = (PWORD)
	    _HeapAllocate ((phwi->dwCodeMemoryLength +
			    phwi->dwDataMemoryLength) * 2, HEAPZEROINIT)))
	return KRETURN_ERROR_GENERIC;

      /* */
      /* Do memory save */
      /* */

      kDspReadWords (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_CODE,
		     phwi->dwCodeMemoryBegin,
		     phwi->dwCodeMemoryLength, phwi->pwSuspendBuffer);

      kDspReadLongWords (devc, phwi->dwBaseIO,
			 MEMTYPE_INTERNAL_DATA,
			 phwi->dwDataMemoryBegin,
			 phwi->dwDataMemoryLength,
			 phwi->pwSuspendBuffer + phwi->dwCodeMemoryLength);

      /* */
      /* Indicate FM is suspended */
      /* */

      phwi->dwFlags |= HWI_FLAG_SUSPENDED;

      return KRETURN_SUCCESS;
    }

  /* */
  /* Fail if the DSP kernel has been unloaded */
  /* */

  if (phwi->dwFlags & HWI_FLAG_UNLOADED)
    return KRETURN_ERROR_UNLOADED;

  /* */
  /* Allocate host memory */
  /* */

  if (!(phwi->pwSuspendBuffer = (PWORD)
	_HeapAllocate ((phwi->dwCodeMemoryLength +
			phwi->dwDataMemoryLength) * 2, HEAPZEROINIT)))
    return KRETURN_ERROR_GENERIC;

  /* */
  /* Make sure DSP kernel is ready to be halted */
  /* */

  if (!kStateExists (devc, phwi, KDATA_HALT_ACKNOWLEDGE, FALSE))
    goto kSuspend_Error;

  /* */
  /* halt the DSP kernel and wait for an acknowledgement */
  /* */

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA, KDATA_HALT_SYNCH_CLIENT, TRUE);

  if (!kStateExists (devc, phwi, KDATA_HALT_ACKNOWLEDGE, TRUE))
    {
      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA, KDATA_HALT_SYNCH_CLIENT, FALSE);

      goto kSuspend_Error;
    }

  /* */
  /* DSP kernel has halted, do memory save */
  /* */
  /* Note: We halt the DSP in order to place it in a known state */
  /* */

  kDspReadWords (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_CODE,
		 phwi->dwCodeMemoryBegin,
		 phwi->dwCodeMemoryLength, phwi->pwSuspendBuffer);

  kDspReadLongWords (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     phwi->dwDataMemoryBegin,
		     phwi->dwDataMemoryLength,
		     phwi->pwSuspendBuffer + phwi->dwCodeMemoryLength);

  /* */
  /* Kernel was successfully suspended, indicate kernel is unloaded */
  /* */

  phwi->dwFlags |= (HWI_FLAG_SUSPENDED | HWI_FLAG_UNLOADED);

  return KRETURN_SUCCESS;

kSuspend_Error:

  /* */
  /* Failure, undo all that was done... */
  /* */

  if (phwi->pwSuspendBuffer)
    {
      _HeapFree (phwi->pwSuspendBuffer, 0);
    }

  return KRETURN_ERROR_GENERIC;

}				/* kSuspendKernel() */

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kResumeKernel */
/* */
/*  Description: */
/*      Resume the kernel and restore its state. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */

KRETURN
kResumeKernel (allegro_devc * devc, PHWI phwi)
{
  BYTE bData;

#ifdef K_DBG
  _Debug_Printf_Service ("kResumeKernel %X\n", phwi);
#endif
  /* */
  /* FM is a special case */
  /* */

  if (phwi->dwFlags & HWI_FLAG_FM_LOADED)
    {
      /* */
      /* Fail if FM isn't suspended */
      /* */

      if (!(phwi->dwFlags & HWI_FLAG_SUSPENDED))
	return KRETURN_ERROR_GENERIC;

      /* */
      /* Do memory restore */
      /* */

      bData = kDspHalt (devc, phwi->dwBaseIO);

      if (!phwi->pwSuspendBuffer)
	return KRETURN_ERROR_GENERIC;

      kDspWriteWords (devc, phwi->dwBaseIO,
		      MEMTYPE_INTERNAL_CODE,
		      phwi->dwCodeMemoryBegin,
		      phwi->dwCodeMemoryLength, phwi->pwSuspendBuffer);

      kDspWriteLongWords (devc, phwi->dwBaseIO,
			  MEMTYPE_INTERNAL_DATA,
			  phwi->dwDataMemoryBegin,
			  phwi->dwDataMemoryLength,
			  phwi->pwSuspendBuffer + phwi->dwCodeMemoryLength);

      _HeapFree (phwi->pwSuspendBuffer, 0);

      phwi->pwSuspendBuffer = 0;

      kDspReset (devc, phwi->dwBaseIO, bData);

      /* Indicate FM is no longer suspended */

      phwi->dwFlags &= ~HWI_FLAG_SUSPENDED;

      return KRETURN_SUCCESS;
    }

  /* */
  /* Fail if the kernel isn't suspended */
  /* */

  if (!(phwi->dwFlags & HWI_FLAG_SUSPENDED))
    return KRETURN_ERROR_GENERIC;

  /* */
  /* Do memory restore */
  /* */

  bData = kDspHalt (devc, phwi->dwBaseIO);

  if (!phwi->pwSuspendBuffer)
    return KRETURN_ERROR_GENERIC;

  kDspWriteWords (devc, phwi->dwBaseIO,
		  MEMTYPE_INTERNAL_CODE,
		  phwi->dwCodeMemoryBegin,
		  phwi->dwCodeMemoryLength, phwi->pwSuspendBuffer);

  kDspWriteLongWords (devc, phwi->dwBaseIO,
		      MEMTYPE_INTERNAL_DATA,
		      phwi->dwDataMemoryBegin,
		      phwi->dwDataMemoryLength,
		      phwi->pwSuspendBuffer + phwi->dwCodeMemoryLength);

  _HeapFree (phwi->pwSuspendBuffer, 0);

  phwi->pwSuspendBuffer = 0;

  kRestartStreams (devc, phwi);

  /* */
  /* all done, reset halt request flag to resume DSP kernel execution */
  /* */

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA, KDATA_HALT_SYNCH_CLIENT, FALSE);

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA, KDATA_HALT_ACKNOWLEDGE, FALSE);

  kDspReset (devc, phwi->dwBaseIO, bData);

  /* Indicate kernel is loaded */

  phwi->dwFlags &= ~(HWI_FLAG_SUSPENDED | HWI_FLAG_UNLOADED);

  return KRETURN_SUCCESS;

}				/* kResumeKernel() */

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kOpenInstance */
/* */
/*  Description: */
/*      Open a client instance. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      DWORD dwClient */
/*         Client ID */
/* */
/*      DWORD dwFlags */
/*         KOPENCLOSE_XXX flags indicating open to perform */
/* */
/*      DWORD dwLen */
/*         Length in bytes of data area of CLIENT_INST (zero allowed) */
/* */
/*      PCLIENT_INST *ppClient_Inst */
/*         Pointer to PCLIENT_INST that will contain the instance pointer */
/* */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */

KRETURN kOpenInstance
  (allegro_devc * devc, PHWI phwi,
   DWORD dwClient, DWORD dwFlags, DWORD dwLen, PCLIENT_INST * ppClient_Inst)
{
  PCLIENT_INST pClient_Inst = NULL;
  PCLIENT_BIN pClient_Bin = NULL;

#ifdef K_DBG
  _Debug_Printf_Service ("kOpenInstance %X %X %X %X\n", phwi,
			 dwClient, dwFlags, dwLen);
#endif
  /* */
  /* Fail if the DSP kernel has been unloaded */
  /* */

  if (phwi->dwFlags & HWI_FLAG_UNLOADED)
    return KRETURN_ERROR_UNLOADED;

#ifndef NT_MODEL
  /* */
  /* FM client is a special case */
  /* */
  /* The FM client isn't really a client of the DSP kernel.  It requires */
  /* the entire DSP to execute therefore the DSP kernel must be swapped */
  /* out of the DSP before it can run.  When FM is closed the DSP kernel */
  /* is reloaded into the DSP. */
  /* */

  if (dwClient == CLIENT_FM)
    {
      DWORD dwClientMasks;
      DWORD dwConnectMasks;
      BYTE bData;

      /* Fail if the DSP kernel is not idle */

      if (kQueryActivity (devc, phwi, &dwClientMasks, &dwConnectMasks) !=
	  KRETURN_SUCCESS)
	return KRETURN_ERROR_GENERIC;

      if (dwClientMasks || dwConnectMasks)
	return KRETURN_ERROR_BUSY;

      /* */
      /* Allocate host memory */
      /* */

      if (!(pClient_Inst = (PCLIENT_INST)
	    _HeapAllocate (sizeof (CLIENT_INST) + dwLen, HEAPZEROINIT)))
	return KRETURN_ERROR_GENERIC;

      pClient_Inst->dwClient = dwClient;

      *ppClient_Inst = pClient_Inst;

      /* download FM client to DSP memory */

      bData = kDspHalt (devc, phwi->dwBaseIO);

      kDisableFMMap (devc, phwi->dwBaseIO, FALSE);

      kDspWriteWords (devc, phwi->dwBaseIO,
		      MEMTYPE_INTERNAL_CODE,
		      phwi->dwCodeMemoryBegin,
		      (gsFMVectCode.dwLengthVect + 1) / 2,
		      gsFMVectCode.pwBinVect);

      kDspWriteWords (devc, phwi->dwBaseIO,
		      MEMTYPE_INTERNAL_CODE,
		      gsFMVectCode.dwCodeAddress,
		      (gsFMVectCode.dwLengthCode + 1) / 2,
		      gsFMVectCode.pwBinCode);

      kDspWriteWords (devc, phwi->dwBaseIO,
		      MEMTYPE_INTERNAL_DATA,
		      phwi->dwDataMemoryBegin,
		      (gsFMVectCode.dwLengthData + 1) / 2,
		      gsFMVectCode.pwBinData);

      kDspWriteWords (devc, phwi->dwBaseIO,
		      MEMTYPE_INTERNAL_DATA,
		      gsFMVectCode.dwData2Address,
		      (gsFMVectCode.dwLengthData2 + 1) / 2,
		      gsFMVectCode.pwBinData2);

      kDspReset (devc, phwi->dwBaseIO, bData);

      /* FM client was successfully loaded, indicate kernel is unloaded */

      phwi->dwFlags |= (HWI_FLAG_FM_LOADED | HWI_FLAG_UNLOADED);

      return KRETURN_SUCCESS;
    }
#endif

  /* */
  /* Do open/close synchronization */
  /* */
  /* Generally, an open can safely be done except just after a close. */
  /* The reason a close might cause a problem is that when a client */
  /* requests a close, the host-side may close but the DSP-side */
  /* may be executing AT THAT MOMENT.  This means the host-side thinks */
  /* resources that were formally allocated are now free, whereas on */
  /* the DSP-side they actually are still in use.  So if an open is done, */
  /* and the open happens to be assigned some of these same resources, a */
  /* resource conflict arises.  For example, the executable of the open */
  /* client might get downloaded over the still executing close client, */
  /* likely causing a DSP crash. */
  /* */
  /* One way to overcome this danger is by making sure a client task */
  /* switch has occured SINCE THE CLOSE WAS COMPLETED.  A task switch */
  /* covers the following cases: */
  /* */
  /* 1) A DSP-side client is executing just as the kernel closes it. */
  /*    A subsequent task switch means this client is no longer running, */
  /*    so the close is complete and the freed resources are available. */
  /* */
  /* 2) A DSP-side client is idle when the kernel closes it.  A */
  /*    subsequent task switch to this client either means the client */
  /*    doesn't run at all (client closed) or an instance is not */
  /*    processed at all (instance closed).  Thus the resources */
  /*    previously used by the client or instance are freed. */
  /* */

  if (!kStateExists (devc, phwi, KDATA_TASK_SWITCH, TRUE))
    return KRETURN_ERROR_BUSY;

  /* */
  /* Make sure client and an available instance exist... */
  /* */

  if (dwClient >= NUMBER_OF_CLIENTS)
    return KRETURN_ERROR_GENERIC;

  if (phwi->asClientTable[dwClient].dwReferenceCount >=
      phwi->asClientTable[dwClient].dwMaxReference)
    return KRETURN_ERROR_GENERIC;

  /* */
  /* Allocate host memory */
  /* */

  if (!(pClient_Inst = (PCLIENT_INST)
	_HeapAllocate (sizeof (CLIENT_INST) + dwLen, HEAPZEROINIT)))
    goto kOpen_Error;

  pClient_Inst->dwClient = dwClient;
  pClient_Inst->dwDspDataNumUnits =
    NUM_UNITS (dwLen, phwi->dwDataMemoryUnitLength);

  /* */
  /* Allocate DSP data memory */
  /* */

  if (dwLen)
    {
      if (!(pClient_Inst->pbDspDataMapPtr =
	    kAllocDspMemory (phwi, NULL,
			     phwi->abDataMemoryMap,
			     pClient_Inst->dwDspDataNumUnits)))
	goto kOpen_Error;

      pClient_Inst->dwDspDataClientArea =
      /*LINTED*/
	((pClient_Inst->pbDspDataMapPtr - phwi->abDataMemoryMap) *
	 phwi->dwDataMemoryUnitLength) + phwi->dwDataMemoryBegin;

      /* clear DSP client data memory */

      kDspWriteZeros (devc, phwi->dwBaseIO,
		      MEMTYPE_INTERNAL_DATA,
		      pClient_Inst->dwDspDataClientArea,
		      pClient_Inst->dwDspDataNumUnits *
		      phwi->dwDataMemoryUnitLength);

      /* download client data binary */

      if (!(pClient_Bin = kBinStructAddress (phwi, dwClient, NULL)))
	goto kOpen_Error;

      /* make sure allocation size can hold data binary */

      if (dwLen < pClient_Bin->dwLengthData)
	goto kOpen_Error;

      kDspWriteWords (devc, phwi->dwBaseIO,
		      MEMTYPE_INTERNAL_DATA,
		      pClient_Inst->dwDspDataClientArea,
		      (pClient_Bin->dwLengthData + 1) / 2,
		      pClient_Bin->pwBinData);

      /* add instance to instance list */
      if (dwFlags & KOPENCLOSE_SYNCHRONOUS)
	/*if ( 0 ) */
	{
	  DWORD dwReturn;

	  /* */
	  /* Make sure DSP kernel is ready to be halted */
	  /* */

	  if (!kStateExists (devc, phwi, KDATA_HALT_ACKNOWLEDGE, FALSE))
	    goto kOpen_Error;

	  /* */
	  /* halt the DSP kernel and wait for an acknowledgement */
	  /* */

	  kDspWriteWord (devc, phwi->dwBaseIO,
			 MEMTYPE_INTERNAL_DATA,
			 KDATA_HALT_SYNCH_CLIENT, TRUE);

	  if (!kStateExists (devc, phwi, KDATA_HALT_ACKNOWLEDGE, TRUE))
	    {
	      kDspWriteWord (devc, phwi->dwBaseIO,
			     MEMTYPE_INTERNAL_DATA,
			     KDATA_HALT_SYNCH_CLIENT, FALSE);

	      goto kOpen_Error;
	    }

	  /* */
	  /* DSP kernel has halted, do open */
	  /* */
	  /* Note: We halt the DSP in order to synchronize the open */
	  /* with the client */
	  /* */

	  dwReturn = kAddListEntry (devc, phwi,
				    kInstanceListAddress (phwi, dwClient),
				    (WORD) phwi->asClientTable[dwClient].
				    dwInstanceListArea,
				    (WORD) (pClient_Inst->
					    dwDspDataClientArea >>
					    DP_SHIFT_COUNT));

	  /* */
	  /* all done, reset halt request flag to resume DSP kernel execution */
	  /* */

	  kDspWriteWord (devc, phwi->dwBaseIO,
			 MEMTYPE_INTERNAL_DATA,
			 KDATA_HALT_SYNCH_CLIENT, FALSE);

	  if (!dwReturn)
	    goto kOpen_Error;
	}
      else
	{
	  if (!kAddListEntry (devc, phwi,
			      kInstanceListAddress (phwi, dwClient),
			      (WORD) phwi->asClientTable[dwClient].
			      dwInstanceListArea,
			      (WORD) (pClient_Inst->
				      dwDspDataClientArea >> DP_SHIFT_COUNT)))
	    goto kOpen_Error;

	}



    }

/*#pragma message("----Assuming individual client code binaries are equal length!") */

  phwi->asClientTable[dwClient].dwDspCodeNumUnits =
    NUM_UNITS (phwi->asClientTable[dwClient].pClient_Bin->dwLengthCode,
	       phwi->dwCodeMemoryUnitLength);

  /* */
  /* Allocate DSP code memory */
  /* */

  if (!phwi->asClientTable[dwClient].dwReferenceCount)
    {
      if (!(phwi->asClientTable[dwClient].pbDspCodeMapPtr =
	    kAllocDspMemory (phwi, dwClient,
			     phwi->abCodeMemoryMap,
			     phwi->asClientTable[dwClient].
			     dwDspCodeNumUnits)))
	goto kOpen_Error;

      phwi->asClientTable[dwClient].dwDspCodeClientArea =
      /*LINTED*/
	((phwi->asClientTable[dwClient].pbDspCodeMapPtr -
	  phwi->abCodeMemoryMap) * phwi->dwCodeMemoryUnitLength) +
	phwi->dwCodeMemoryBegin;

      pClient_Inst->dwDspCodeClientArea =
	phwi->asClientTable[dwClient].dwDspCodeClientArea;

      /* clear DSP client code memory */

      kDspWriteZeros (devc, phwi->dwBaseIO,
		      MEMTYPE_INTERNAL_CODE,
		      phwi->asClientTable[dwClient].dwDspCodeClientArea,
		      phwi->asClientTable[dwClient].dwDspCodeNumUnits *
		      phwi->dwCodeMemoryUnitLength);

      /* download client code binary */

      if (!(pClient_Bin = kBinStructAddress (phwi, dwClient,
					     phwi->asClientTable[dwClient].
					     dwDspCodeClientArea)))
	goto kOpen_Error;

      kDspWriteWords (devc, phwi->dwBaseIO,
		      MEMTYPE_INTERNAL_CODE,
		      phwi->asClientTable[dwClient].dwDspCodeClientArea,
		      (pClient_Bin->dwLengthCode + 1) / 2,
		      pClient_Bin->pwBinCode);

      /* allocate DSP vectors */

      if (!kAllocDspVectors (devc, phwi, dwClient))
	goto kOpen_Error;

      /* add client to task list */

      if (!kAddListEntry (devc, phwi,
			  phwi->awTaskList,
			  KDATA_TASK0,
			  (WORD) phwi->asClientTable[dwClient].
			  dwDspCodeClientArea))
	goto kOpen_Error;
    }
  else
    {
      pClient_Inst->dwDspCodeClientArea =
	phwi->asClientTable[dwClient].dwDspCodeClientArea;
    }

  /* */
  /* Everything worked, increment reference counter */
  /* */

  ++phwi->asClientTable[dwClient].dwReferenceCount;

  *ppClient_Inst = pClient_Inst;
#ifdef K_DBG
  _Debug_Printf_Service ("kOpenInstance OK  %X\n", pClient_Inst);
#endif

  return KRETURN_SUCCESS;

kOpen_Error:

  /* */
  /* Failure, undo all that was done... */
  /* */

  if (!phwi->asClientTable[dwClient].dwReferenceCount)
    {
      if (phwi->asClientTable[dwClient].pbDspCodeMapPtr)
	{
	  kDeallocDspVectors (devc, phwi, dwClient);

	  kDeallocDspMemory (phwi->asClientTable[dwClient].pbDspCodeMapPtr,
			     phwi->asClientTable[dwClient].dwDspCodeNumUnits);
	  phwi->asClientTable[dwClient].pbDspCodeMapPtr = NULL;
	}
    }

  if (pClient_Inst)
    {
      if (pClient_Inst->pbDspDataMapPtr)
	{
	  kRemoveListEntry (devc, phwi,
			    kInstanceListAddress (phwi, dwClient),
			    (WORD) phwi->asClientTable[dwClient].
			    dwInstanceListArea,
			    (WORD) (pClient_Inst->
				    dwDspDataClientArea >> DP_SHIFT_COUNT));

	  kDeallocDspMemory (pClient_Inst->pbDspDataMapPtr,
			     pClient_Inst->dwDspDataNumUnits);
	}

      _HeapFree (pClient_Inst, 0);
    }
#ifdef K_DBG
  _Debug_Printf_Service ("kOpenInstance Failed\n");
#endif

  return KRETURN_ERROR_GENERIC;

}				/* kOpenInstance() */

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kCloseInstance */
/* */
/*  Description: */
/*      Close a client instance. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      PCLIENT_INST pClient_Inst */
/*         Pointer to client instance */
/* */
/*      DWORD dwFlags */
/*         KOPENCLOSE_XXX flags indicating close to perform */
/* */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */

KRETURN
kCloseInstance (allegro_devc * devc, PHWI phwi, PCLIENT_INST pClient_Inst,
		DWORD dwFlags)
{
  DWORD dwClient = pClient_Inst->dwClient;

#ifdef K_DBG
  _Debug_Printf_Service ("kCloseInstance %X %X %X\n", phwi,
			 pClient_Inst, dwFlags);


#endif
#ifndef NT_MODEL
  /* */
  /* FM client is a special case */
  /* */

  if (dwClient == CLIENT_FM)
    {
      /* Fail if the FM client isn't open */

      if (!(phwi->dwFlags & HWI_FLAG_FM_LOADED))
	return KRETURN_ERROR_GENERIC;

      kLoadKernel (devc, phwi, phwi->dwBaseIO, phwi->dwFlags);

      _HeapFree (pClient_Inst, 0);

      /* Indicate kernel is loaded */

      phwi->dwFlags &= ~(HWI_FLAG_FM_LOADED | HWI_FLAG_UNLOADED);

      return KRETURN_SUCCESS;
    }
#endif

  /* */
  /* Fail if the DSP kernel has been unloaded */
  /* */

  if (phwi->dwFlags & HWI_FLAG_UNLOADED)
    return KRETURN_ERROR_UNLOADED;

  /* */
  /* Do DMA synchronization */
  /* */
  /* (see discussion in kOpenInstance) */
  /* */

  if (!kStateExists (devc, phwi, KDATA_DMA_SWITCH, TRUE))
    return KRETURN_ERROR_BUSY;

  /* */
  /* Make sure client and an instance exist... */
  /* */

  if (dwClient >= NUMBER_OF_CLIENTS)
    return KRETURN_ERROR_GENERIC;

  if (!phwi->asClientTable[dwClient].dwReferenceCount)
    return KRETURN_ERROR_GENERIC;

  /* */
  /* Deallocate memory */
  /* */

  if (--phwi->asClientTable[dwClient].dwReferenceCount == 0)
    {
      /* remove client from task list */

      kRemoveListEntry (devc, phwi,
			phwi->awTaskList,
			KDATA_TASK0,
			(WORD) phwi->asClientTable[dwClient].
			dwDspCodeClientArea);

      if (!phwi->asClientTable[dwClient].pbDspCodeMapPtr)
	return KRETURN_ERROR_GENERIC;

      kDeallocDspVectors (devc, phwi, dwClient);

      kDeallocDspMemory (phwi->asClientTable[dwClient].pbDspCodeMapPtr,
			 phwi->asClientTable[dwClient].dwDspCodeNumUnits);
      phwi->asClientTable[dwClient].pbDspCodeMapPtr = NULL;
    }


  if (!pClient_Inst)
    return KRETURN_ERROR_GENERIC;

  if (pClient_Inst->pbDspDataMapPtr)
    {
      if (dwFlags & KOPENCLOSE_SYNCHRONOUS)
	/* if ( 0 ) */
	{
	  /* */
	  /* Make sure DSP kernel is ready to be halted */
	  /* */

	  if (!kStateExists (devc, phwi, KDATA_HALT_ACKNOWLEDGE, FALSE))
	    goto kClose_Error;

	  /* */
	  /* halt the DSP kernel and wait for an acknowledgement */
	  /* */

	  kDspWriteWord (devc, phwi->dwBaseIO,
			 MEMTYPE_INTERNAL_DATA,
			 KDATA_HALT_SYNCH_CLIENT, TRUE);

	  kStateExists (devc, phwi, KDATA_HALT_ACKNOWLEDGE, TRUE);

	kClose_Error:

	  /* */
	  /* DSP kernel has halted, do close */
	  /* */
	  /* Note: We halt the DSP in order to synchronize the close */
	  /* with the client */
	  /* */

	  kRemoveListEntry (devc, phwi,
			    kInstanceListAddress (phwi, dwClient),
			    (WORD) phwi->asClientTable[dwClient].
			    dwInstanceListArea,
			    (WORD) (pClient_Inst->
				    dwDspDataClientArea >> DP_SHIFT_COUNT));

	  /* */
	  /* all done, reset halt request flag to resume DSP kernel execution */
	  /* */

	  kDspWriteWord (devc, phwi->dwBaseIO,
			 MEMTYPE_INTERNAL_DATA,
			 KDATA_HALT_SYNCH_CLIENT, FALSE);
	}
      else
	{
	  kRemoveListEntry (devc, phwi,
			    kInstanceListAddress (phwi, dwClient),
			    (WORD) phwi->asClientTable[dwClient].
			    dwInstanceListArea,
			    (WORD) (pClient_Inst->
				    dwDspDataClientArea >> DP_SHIFT_COUNT));

	}

      kDeallocDspMemory (pClient_Inst->pbDspDataMapPtr,
			 pClient_Inst->dwDspDataNumUnits);
    }

  _HeapFree (pClient_Inst, 0);

  /* */
  /* Do open/close synchronization */
  /* */

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA, KDATA_TASK_SWITCH, FALSE);

#ifdef K_DBG
  _Debug_Printf_Service ("kCloseInstance OK\n");
#endif
  return KRETURN_SUCCESS;

}				/* kCloseInstance() */

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kSwitchClient */
/* */
/*  Description: */
/*      Remove/Add the client from/to the task list. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      PCLIENT_INST pClient_Inst */
/*         Pointer to client instance */
/* */
/*      DWORD dwFlags */
/*         KENABLE_CLIENT flags adding client back to task list */
/*         KDISABLE_CLIENT flags removing client from task list */
/* */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */

KRETURN
kSwitchClient (allegro_devc * devc, PHWI phwi, PCLIENT_INST pClient_Inst,
	       DWORD dwFlags)
{
  DWORD dwClient = pClient_Inst->dwClient;
#ifdef K_DBG
  _Debug_Printf_Service ("kSwitchClient %X %X %X\n", phwi,
			 pClient_Inst, dwFlags);



#endif
  /* */
  /* Fail if the DSP kernel has been unloaded */
  /* */

  if (phwi->dwFlags & HWI_FLAG_UNLOADED)
    return KRETURN_ERROR_UNLOADED;

  /* */
  /* Do DMA synchronization */
  /* */
  /* (see discussion in kOpenInstance) */
  /* */

  if (!kStateExists (devc, phwi, KDATA_DMA_SWITCH, TRUE))
    return KRETURN_ERROR_BUSY;

  /* */
  /* Make sure client and an instance exist... */
  /* */

  if (dwClient >= NUMBER_OF_CLIENTS)
    return KRETURN_ERROR_GENERIC;

  if (phwi->asClientTable[dwClient].dwReferenceCount != 1)
    return KRETURN_ERROR_GENERIC;

  if (dwFlags == KDISABLE_CLIENT)
    {
      /* remove client from task list */

      kRemoveListEntry (devc, phwi,
			phwi->awTaskList,
			KDATA_TASK0,
			(WORD) phwi->asClientTable[dwClient].
			dwDspCodeClientArea);

    }
  else
    {
      if (dwFlags == KENABLE_CLIENT)
	{
	  /* add client to task list */

	  if (!kAddListEntry (devc, phwi,
			      phwi->awTaskList,
			      KDATA_TASK0,
			      (WORD) phwi->asClientTable[dwClient].
			      dwDspCodeClientArea))
	    {
	      return KRETURN_ERROR_GENERIC;
	    }

	}
      else
	{
	  return KRETURN_ERROR_GENERIC;
	}			/* endif */
    }				/* endif */

#ifdef K_DBG
  _Debug_Printf_Service ("kSwitchClient OK\n");
#endif

  return KRETURN_SUCCESS;

}				/* kSwitchClient() */

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kSetInstanceReady */
/* */
/*  Description: */
/*      Set a client instance to the ready state. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      PCLIENT_INST pClient_Inst */
/*         Pointer to client instance */
/* */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */

KRETURN
kSetInstanceReady (allegro_devc * devc, PHWI phwi, PCLIENT_INST pClient_Inst)
{
  DWORD dwClient = pClient_Inst->dwClient;

#ifdef K_DBG
  _Debug_Printf_Service ("kSetInstanceReady %X %x\n", phwi, pClient_Inst);
#endif
  /* */
  /* Fail if the DSP kernel has been unloaded */
  /* */

  if (phwi->dwFlags & HWI_FLAG_UNLOADED)
    return KRETURN_ERROR_UNLOADED;

  /* */
  /* Make sure client and an instance exist... */
  /* */

  if (dwClient >= NUMBER_OF_CLIENTS)
    return KRETURN_ERROR_GENERIC;

  if (!phwi->asClientTable[dwClient].dwReferenceCount)
    return KRETURN_ERROR_GENERIC;

  /* */
  /* Set the instance ready flag */
  /* */

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA,
		 pClient_Inst->
		 dwDspDataClientArea + CDATA_INSTANCE_READY, TRUE);

  return KRETURN_SUCCESS;

}				/* kSetInstanceReady() */

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kSetInstanceNotReady */
/* */
/*  Description: */
/*      Set a client instance to the not ready state. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      PCLIENT_INST pClient_Inst */
/*         Pointer to client instance */
/* */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */

KRETURN
kSetInstanceNotReady (allegro_devc * devc, PHWI phwi,
		      PCLIENT_INST pClient_Inst)
{
  DWORD dwClient = pClient_Inst->dwClient;

#ifdef K_DBG
  _Debug_Printf_Service ("kSetInstanceNotReady %X %x\n", phwi, pClient_Inst);
#endif
  /* */
  /* Fail if the DSP kernel has been unloaded */
  /* */

  if (phwi->dwFlags & HWI_FLAG_UNLOADED)
    return KRETURN_ERROR_UNLOADED;

  /* */
  /* Make sure client and an instance exist... */
  /* */

  if (dwClient >= NUMBER_OF_CLIENTS)
    return KRETURN_ERROR_GENERIC;

  if (!phwi->asClientTable[dwClient].dwReferenceCount)
    return KRETURN_ERROR_GENERIC;

  /* */
  /* Clear the instance ready flag */
  /* */

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA,
		 pClient_Inst->
		 dwDspDataClientArea + CDATA_INSTANCE_READY, FALSE);

  return KRETURN_SUCCESS;

}				/* kSetInstanceNotReady() */

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kStartTransfer */
/* */
/*  Description: */
/*      Start data transfer to/from client instance data memory. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      PCLIENT_INST pClient_Inst */
/*         Pointer to client instance */
/* */
/*      DWORD dwAutoRepeat */
/*         TRUE if auto-repeat buffer, FALSE if one-shot buffer */
/* */
/*      DWORD dwHostSrcBufferAddr */
/*         Host source buffer physical address */
/* */
/*      DWORD dwHostSrcBufferLen */
/*         Host source buffer length in bytes */
/* */
/*      DWORD dwHostDstBufferAddr */
/*         Host destination buffer linear address */
/* */
/*      DWORD dwHostDstBufferLen */
/*         Host destination buffer length in bytes */
/* */
/*      DWORD dwDSPInBufferAddr */
/*         DSP input buffer absolute address */
/* */
/*      DWORD dwDSPInBufferLen */
/*         DSP input buffer length in bytes */
/* */
/*      DWORD dwDSPOutBufferAddr */
/*         DSP output buffer absolute address */
/* */
/*      DWORD dwDSPOutBufferLen */
/*         DSP output buffer length in bytes */
/* */
/*      DWORD dwDSPInConnection */
/*         KCONNECT_XXX indicating input connection */
/* */
/*      DWORD dwDSPOutConnection */
/*         KCONNECT_XXX indicating output connection */
/* */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */

KRETURN kStartTransfer
  (allegro_devc * devc, PHWI phwi,
   PCLIENT_INST pClient_Inst,
   DWORD dwAutoRepeat,
   DWORD dwHostSrcBufferAddr,
   DWORD dwHostSrcBufferLen,
   DWORD dwHostDstBufferAddr,
   DWORD dwHostDstBufferLen,
   DWORD dwDSPInBufferAddr,
   DWORD dwDSPInBufferLen,
   DWORD dwDSPOutBufferAddr,
   DWORD dwDSPOutBufferLen, DWORD dwDSPInConnection, DWORD dwDSPOutConnection)
{
  DWORD dwClient = pClient_Inst->dwClient;
  DWORD dwDspDataClientArea = pClient_Inst->dwDspDataClientArea;

#ifdef K_DBG
  _Debug_Printf_Service
    ("kStartTransfer %X %X %X %X %X %X %X %X %X %X %X %X %X\n", phwi,
     pClient_Inst, dwAutoRepeat, dwHostSrcBufferAddr, dwHostSrcBufferLen,
     dwHostDstBufferAddr, dwHostDstBufferLen, dwDSPInBufferAddr,
     dwDSPInBufferLen, dwDSPOutBufferAddr, dwDSPOutBufferLen,
     dwDSPInConnection, dwDSPOutConnection);

#endif
  /* */
  /* Fail if the DSP kernel has been unloaded */
  /* */

  if (phwi->dwFlags & HWI_FLAG_UNLOADED)
    return KRETURN_ERROR_UNLOADED;


  if ((dwDSPInConnection == KCONNECT_DMA) ||
      (dwDSPOutConnection == KCONNECT_DMA))
    {
      /* */
      /* Do DMA synchronization */
      /* */
      /* (see discussion in kOpenInstance) */
      /* */

      if (!kStateExists (devc, phwi, KDATA_DMA_SWITCH, TRUE))
	return KRETURN_ERROR_BUSY;
    }

  /* */
  /* Make sure client and an instance exist... */
  /* */

  if (dwClient >= NUMBER_OF_CLIENTS)
    return KRETURN_ERROR_GENERIC;

  if (!phwi->asClientTable[dwClient].dwReferenceCount)
    return KRETURN_ERROR_GENERIC;

  /* */
  /* Verify input connection parameters */
  /* */

  if ((dwDSPInConnection != KCONNECT_NONE) &&
      (dwDSPInConnection != KCONNECT_SAME))
    {
      if (!dwDSPInBufferLen)
	return KRETURN_ERROR_GENERIC;

      /* DSP buffer length must be block multiple */

      if (dwDSPInBufferLen & (DMA_BLOCK_LENGTH - 1))
	return KRETURN_ERROR_GENERIC;

      if (dwDSPInConnection == KCONNECT_DMA)
	{
	  /* host buffer must be DWORD aligned */

	  if (dwHostSrcBufferAddr & (sizeof (DWORD) - 1))
	    return KRETURN_ERROR_GENERIC;

	  if (!dwHostSrcBufferLen)
	    return KRETURN_ERROR_GENERIC;
	}
    }

  /* */
  /* Verify output connection parameters */
  /* */

  if ((dwDSPOutConnection != KCONNECT_NONE) &&
      (dwDSPOutConnection != KCONNECT_SAME))
    {
      if (!dwDSPOutBufferLen)
	return KRETURN_ERROR_GENERIC;

      /* DSP buffer length must be block multiple */

      if (dwDSPOutBufferLen & (DMA_BLOCK_LENGTH - 1))
	return KRETURN_ERROR_GENERIC;

      if ((dwDSPOutConnection == KCONNECT_DMA) ||
	  (dwDSPOutConnection == KCONNECT_PIO))
	{
	  /* host buffer must be DWORD aligned */

	  if (dwHostDstBufferAddr & (sizeof (DWORD) - 1))
	    return KRETURN_ERROR_GENERIC;

	  if (!dwHostDstBufferLen)
	    return KRETURN_ERROR_GENERIC;
	}
    }

  pClient_Inst->dwHostSrcBufferAddr = dwHostSrcBufferAddr;
  pClient_Inst->dwHostSrcBufferLen = dwHostSrcBufferLen;
  pClient_Inst->dwHostDstBufferAddr = dwHostDstBufferAddr;
  pClient_Inst->dwHostDstBufferLen = dwHostDstBufferLen;
  pClient_Inst->dwHostDstCurrent = dwHostDstBufferAddr;
  pClient_Inst->dwDSPOutBufferAddr = dwDSPOutBufferAddr;
  pClient_Inst->dwDSPOutBufferLen = dwDSPOutBufferLen;
  pClient_Inst->dwDSPInConnection = dwDSPInConnection;
  pClient_Inst->dwDSPOutConnection = dwDSPOutConnection;

  /* */
  /* Write parameters to client instance memory */
  /* */

  if (dwDSPInConnection == KCONNECT_DMA)
    {
      /* host source */

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     dwDspDataClientArea + CDATA_HOST_SRC_ADDRL,
		     LOWORD (dwHostSrcBufferAddr));

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     dwDspDataClientArea + CDATA_HOST_SRC_ADDRH,
		     HIWORD (dwHostSrcBufferAddr));

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     dwDspDataClientArea + CDATA_HOST_SRC_END_PLUS_1L,
		     LOWORD (dwHostSrcBufferAddr + dwHostSrcBufferLen));

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     dwDspDataClientArea + CDATA_HOST_SRC_END_PLUS_1H,
		     HIWORD (dwHostSrcBufferAddr + dwHostSrcBufferLen));

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     dwDspDataClientArea + CDATA_HOST_SRC_CURRENTL,
		     LOWORD (dwHostSrcBufferAddr));

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     dwDspDataClientArea + CDATA_HOST_SRC_CURRENTH,
		     HIWORD (dwHostSrcBufferAddr));
    }

/*#pragma message("----Stream loopback not supported!") */

  else if (dwDSPOutConnection == KCONNECT_DMA)
    {
      /* host destination */

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     dwDspDataClientArea + CDATA_HOST_SRC_ADDRL,
		     LOWORD (dwHostDstBufferAddr));

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     dwDspDataClientArea + CDATA_HOST_SRC_ADDRH,
		     HIWORD (dwHostDstBufferAddr));

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     dwDspDataClientArea + CDATA_HOST_SRC_END_PLUS_1L,
		     LOWORD (dwHostDstBufferAddr + dwHostDstBufferLen));

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     dwDspDataClientArea + CDATA_HOST_SRC_END_PLUS_1H,
		     HIWORD (dwHostDstBufferAddr + dwHostDstBufferLen));

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     dwDspDataClientArea + CDATA_HOST_SRC_CURRENTL,
		     LOWORD (dwHostDstBufferAddr));

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     dwDspDataClientArea + CDATA_HOST_SRC_CURRENTH,
		     HIWORD (dwHostDstBufferAddr));
    }

  /* DSP input */

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA,
		 dwDspDataClientArea + CDATA_IN_BUF_BEGIN,
		 (WORD) dwDSPInBufferAddr);

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA,
		 dwDspDataClientArea + CDATA_IN_BUF_END_PLUS_1,
		 (WORD) (dwDSPInBufferAddr + (dwDSPInBufferLen / 2)));

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA,
		 dwDspDataClientArea + CDATA_IN_BUF_HEAD,
		 (WORD) dwDSPInBufferAddr);

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA,
		 dwDspDataClientArea + CDATA_IN_BUF_TAIL,
		 (WORD) dwDSPInBufferAddr);

  /* DSP output */

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA,
		 dwDspDataClientArea + CDATA_OUT_BUF_BEGIN,
		 (WORD) dwDSPOutBufferAddr);

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA,
		 dwDspDataClientArea + CDATA_OUT_BUF_END_PLUS_1,
		 (WORD) (dwDSPOutBufferAddr + (dwDSPOutBufferLen / 2)));

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA,
		 dwDspDataClientArea + CDATA_OUT_BUF_HEAD,
		 (WORD) dwDSPOutBufferAddr);

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA,
		 dwDspDataClientArea + CDATA_OUT_BUF_TAIL,
		 (WORD) dwDSPOutBufferAddr);

  /* connect the input and output */

  if (dwDSPInConnection == KCONNECT_DMA)
    {
      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     dwDspDataClientArea + CDATA_DMA_CONTROL,
		     (WORD) (dwAutoRepeat ?
			     (DMACONTROL_AUTOREPEAT + DMAC_PAGE3_SELECTOR +
			      DMAC_BLOCKF_SELECTOR) : (DMAC_PAGE3_SELECTOR +
						       DMAC_BLOCKF_SELECTOR)));
    }

/*#pragma message("----Stream loopback not supported!") */

  /* */
  /* The kernel currently does not support stream loopback. */
  /* Loopback means data is tranfered via DMA from the host to the */
  /* DSP, then the data is transfered via DMA from the DSP back to */
  /* the host.  While the data is in DSP memory it is filtered in */
  /* some way.  Thus the DSP acts as a hardware accelerator by */
  /* off-loading filter processing from the host processor. */
  /* */

  else if (dwDSPOutConnection == KCONNECT_DMA)
    {
      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     dwDspDataClientArea + CDATA_DMA_CONTROL,
		     (WORD) (dwAutoRepeat ?
			     (DMACONTROL_DIRECTION + DMACONTROL_AUTOREPEAT +
			      DMAC_PAGE3_SELECTOR +
			      DMAC_BLOCKF_SELECTOR) : (DMACONTROL_DIRECTION +
						       DMAC_PAGE3_SELECTOR +
						       DMAC_BLOCKF_SELECTOR)));
    }

  if (!kConnectInputOutput (devc, phwi,
			    dwDSPInConnection,
			    dwDSPOutConnection, dwDspDataClientArea))
    return KRETURN_ERROR_GENERIC;

#ifdef K_DBG
  _Debug_Printf_Service ("kStartTransfer OK\n");
#endif
  return KRETURN_SUCCESS;

}				/* kStartTransfer() */

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kStopTransfer */
/* */
/*  Description: */
/*      Stop data transfer to/from client instance data memory. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      PCLIENT_INST pClient_Inst */
/*         Pointer to client instance */
/* */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */

KRETURN
kStopTransfer (allegro_devc * devc, PHWI phwi, PCLIENT_INST pClient_Inst)
{
  DWORD dwClient = pClient_Inst->dwClient;

#ifdef K_DBG
  _Debug_Printf_Service ("kStopTransfer %X %X\n", phwi, pClient_Inst);
#endif
  /* */
  /* Fail if the DSP kernel has been unloaded */
  /* */

  if (phwi->dwFlags & HWI_FLAG_UNLOADED)
    return KRETURN_ERROR_UNLOADED;

  /* */
  /* Make sure client and an instance exist... */
  /* */

  if (dwClient >= NUMBER_OF_CLIENTS)
    return KRETURN_ERROR_GENERIC;

  if (!phwi->asClientTable[dwClient].dwReferenceCount)
    return KRETURN_ERROR_GENERIC;

  /* */
  /* disconnect the input and output */
  /* */

  if (!kDisconnectInputOutput (devc, phwi,
			       pClient_Inst->dwDSPInConnection,
			       pClient_Inst->dwDSPOutConnection,
			       pClient_Inst->dwDspDataClientArea))
    return KRETURN_ERROR_GENERIC;

#ifdef K_DBG
  _Debug_Printf_Service ("kStopTransfer OK\n");
#endif
  return KRETURN_SUCCESS;

}				/* kStopTransfer() */

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kAlterTransfer */
/* */
/*  Description: */
/*      Alter a client's DMA transfer. */
/* */
/*      When altering a client's current stream position, a position */
/*      equal to 0 indicates the first byte in the buffer.  A position */
/*      equal to dwHostXXXBufferLen-1 indicates the last byte in the */
/*      buffer. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      PCLIENT_INST pClient_Inst */
/*         Pointer to client instance */
/* */
/*      DWORD dwFlags */
/*         KALTER_XXX flags indicating alterations to perform */
/* */
/*      DWORD dwAutoRepeat */
/*         TRUE if auto-repeat buffer, FALSE if one-shot buffer */
/* */
/*      DWORD dwPosition */
/*         The byte position to set */
/* */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */

KRETURN kAlterTransfer
  (allegro_devc * devc, PHWI phwi,
   PCLIENT_INST pClient_Inst,
   DWORD dwFlags, DWORD dwAutoRepeat, DWORD dwPosition)
{
  DWORD dwClient = pClient_Inst->dwClient;
  WORD wDmaControl;

#ifdef K_DBG
  _Debug_Printf_Service ("kAlterTransfer %X %X %X %X %X\n", phwi,
			 pClient_Inst, dwFlags, dwAutoRepeat, dwPosition);
#endif
  /* */
  /* Fail if the DSP kernel has been unloaded */
  /* */

  if (phwi->dwFlags & HWI_FLAG_UNLOADED)
    return KRETURN_ERROR_UNLOADED;

  /* */
  /* Make sure client and an instance exist... */
  /* */

  if (dwClient >= NUMBER_OF_CLIENTS)
    return KRETURN_ERROR_GENERIC;

  if (!phwi->asClientTable[dwClient].dwReferenceCount)
    return KRETURN_ERROR_GENERIC;

  /* */
  /* Make sure DSP kernel is ready to be halted */
  /* */

  if (!kStateExists (devc, phwi, KDATA_HALT_ACKNOWLEDGE, FALSE))
    return KRETURN_ERROR_BUSY;

  /* */
  /* halt the DSP kernel and wait for an acknowledgement */
  /* */

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA, KDATA_HALT_SYNCH_DMA, TRUE);

  if (!kStateExists (devc, phwi, KDATA_HALT_ACKNOWLEDGE, TRUE))
    {
      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA, KDATA_HALT_SYNCH_DMA, FALSE);

      return KRETURN_ERROR_BUSY;
    }

  /* */
  /* DSP kernel has halted, do DMA alterations */
  /* */
  /* Note: We halt the DSP in order to synchronize the alterations */
  /* with the DMA state machine */
  /* */

  if (dwFlags & KALTER_AUTOREPEAT)
    {
      wDmaControl = kDspReadWord (devc, phwi->dwBaseIO,
				  MEMTYPE_INTERNAL_DATA,
				  pClient_Inst->dwDspDataClientArea +
				  CDATA_DMA_CONTROL);

      wDmaControl &= ~(DMACONTROL_STOPPED | DMACONTROL_AUTOREPEAT);

      if (dwAutoRepeat)
	wDmaControl |= DMACONTROL_AUTOREPEAT;

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     pClient_Inst->dwDspDataClientArea + CDATA_DMA_CONTROL,
		     wDmaControl);
    }

  if (dwFlags & KALTER_POSITION)
    {
      /* */
      /* Position alterations are only permitted on playback streams... */
      /* */

      if (pClient_Inst->dwDSPInConnection == KCONNECT_DMA)
	{
	  dwPosition += pClient_Inst->dwHostSrcBufferAddr;

	  kDspWriteWord (devc, phwi->dwBaseIO,
			 MEMTYPE_INTERNAL_DATA,
			 pClient_Inst->dwDspDataClientArea +
			 CDATA_HOST_SRC_CURRENTL, LOWORD (dwPosition));

	  kDspWriteWord (devc, phwi->dwBaseIO,
			 MEMTYPE_INTERNAL_DATA,
			 pClient_Inst->dwDspDataClientArea +
			 CDATA_HOST_SRC_CURRENTH, HIWORD (dwPosition));
	}
    }

  /* */
  /* all done, reset halt request flag to resume DSP kernel execution */
  /* */

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA, KDATA_HALT_SYNCH_DMA, FALSE);

  return KRETURN_SUCCESS;

}				/* kAlterTransfer() */

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kSwitchPINConnection */
/* */
/*  Description: */
/*      Switch data transfer PIN connection to/from client instance data memory. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      PCLIENT_INST pClient_Inst */
/*         Pointer to client instance */
/* */
/*      DWORD dwDSPInConnection */
/*         KCONNECT_XXX indicating input connection */
/* */
/*      DWORD dwDSPOutConnection */
/*         KCONNECT_XXX indicating output connection */
/* */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */

KRETURN kSwitchPINConnection
  (allegro_devc * devc, PHWI phwi,
   PCLIENT_INST pClient_Inst,
   DWORD dwDSPInConnection, DWORD dwDSPOutConnection)
{
  DWORD dwClient = pClient_Inst->dwClient;
  DWORD dwDspDataClientArea = pClient_Inst->dwDspDataClientArea;
#ifdef K_DBG
  _Debug_Printf_Service ("kSwitchPINConnection %X %X %X %X \n",
			 phwi,
			 pClient_Inst, dwDSPInConnection, dwDSPOutConnection);

#endif

  /* */
  /* Fail if the DSP kernel has been unloaded */
  /* */

  if (phwi->dwFlags & HWI_FLAG_UNLOADED)
    return KRETURN_ERROR_UNLOADED;

  /* */
  /* Make sure connections are valid... */
  /* */

  if (dwDSPInConnection >= NUMBER_OF_CONNECTIONS)
    return KRETURN_ERROR_GENERIC;

  if (dwDSPOutConnection >= NUMBER_OF_CONNECTIONS)
    return KRETURN_ERROR_GENERIC;

  if ((dwDSPInConnection == KCONNECT_DMA) ||
      (dwDSPOutConnection == KCONNECT_DMA))
    {
      /* */
      /* Do DMA synchronization */
      /* */
      /* DMA engine is not switchable at this moment!!! */
      /* */

      if (!kStateExists (devc, phwi, KDATA_DMA_SWITCH, TRUE))
	return KRETURN_ERROR_BUSY;
    }

  /* */
  /* Make sure client and an instance exist... */
  /* */

  if (dwClient >= NUMBER_OF_CLIENTS)
    return KRETURN_ERROR_GENERIC;

  if (!phwi->asClientTable[dwClient].dwReferenceCount)
    return KRETURN_ERROR_GENERIC;


  /* */
  /* disconnect the input and output first */
  /* */

  if (!kDisconnectInputOutput (devc, phwi,
			       pClient_Inst->dwDSPInConnection,
			       pClient_Inst->dwDSPOutConnection,
			       pClient_Inst->dwDspDataClientArea))
    return KRETURN_ERROR_GENERIC;


  /* */
  /*  update PIN connection  */
  /* */

  if (dwDSPInConnection != KCONNECT_SAME)
    pClient_Inst->dwDSPInConnection = dwDSPInConnection;

  if (dwDSPOutConnection != KCONNECT_SAME)
    pClient_Inst->dwDSPOutConnection = dwDSPOutConnection;


  /* */
  /*  Finally, connect updated PIN  */
  /* */

  if (!kConnectInputOutput (devc, phwi,
			    pClient_Inst->dwDSPInConnection,
			    pClient_Inst->dwDSPOutConnection,
			    dwDspDataClientArea))
    return KRETURN_ERROR_GENERIC;


#ifdef K_DBG
  _Debug_Printf_Service ("kSwitchPINConnection OK\n");
#endif
  return KRETURN_SUCCESS;

}				/* kSwitchPINConnection() */

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kQueryActivity */
/* */
/*  Description: */
/*      Indicate what clients and/or connections are active */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      PDWORD pdwClientMasks */
/*         Pointer to DWORD that will contain MASK_CLIENT_XXX masks */
/* */
/*      PDWORD pdwConnectMasks */
/*         Pointer to DWORD that will contain MASK_KCONNECT_XXX masks */
/* */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */

KRETURN kQueryActivity
  (allegro_devc * devc, PHWI phwi, PDWORD pdwClientMasks,
   PDWORD pdwConnectMasks)
{
  DWORD dwClient;

#ifdef K_DBG
  _Debug_Printf_Service ("kQueryActivity %X %X %X\n", phwi,
			 pdwClientMasks, pdwConnectMasks);


#endif
  /* */
  /* Fail if the DSP kernel has been unloaded */
  /* */

  if (phwi->dwFlags & HWI_FLAG_UNLOADED)
    return KRETURN_ERROR_UNLOADED;

  /* */
  /* Check if any client instance is defined */
  /* */

  *pdwClientMasks = 0;

  for (dwClient = 0; dwClient < NUMBER_OF_CLIENTS; ++dwClient)
    {
      if (phwi->asClientTable[dwClient].dwReferenceCount)
	{
	  *pdwClientMasks |= (1 << dwClient);
	}
    }

  /* */
  /* Check if any connections are defined */
  /* */

  *pdwConnectMasks = 0;

  if (phwi->awVirtualDMAList[0])
    *pdwConnectMasks |= MASK_KCONNECT_DMA;

  if (phwi->awVirtualADC1List[0])
    *pdwConnectMasks |= MASK_KCONNECT_ADC1;

  if (phwi->awVirtualADC2List[0])
    *pdwConnectMasks |= MASK_KCONNECT_ADC2;

  if (phwi->awVirtualI2SList[0])
    *pdwConnectMasks |= MASK_KCONNECT_I2S;

  if (phwi->awVirtualCHIList[0])
    *pdwConnectMasks |= MASK_KCONNECT_CHI;

  if (kDspReadWord
      (devc, phwi->dwBaseIO, MEMTYPE_INTERNAL_DATA, KDATA_SPDIF_XFER))
    *pdwConnectMasks |= MASK_KCONNECT_SPDIF;

  if (phwi->awVirtualMIXERList[0])
    *pdwConnectMasks |= MASK_KCONNECT_MIXER;

#if 0
#if (NUMBER_OF_CONNECTIONS != 0x10)
#error Assumption about number of connections failed.
#endif
#endif

  return KRETURN_SUCCESS;

}				/* kQueryActivity() */

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kSetTimer */
/* */
/*  Description: */
/*      Set the DSP to Host interrupt time interval */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      DWORD dwTimeInterval */
/*         Number of APU interrupts per DSP to Host interrupt */
/* */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */

KRETURN
kSetTimer (allegro_devc * devc, PHWI phwi, DWORD dwTimeInterval)
{
#ifdef K_DBG
  _Debug_Printf_Service ("kSetTimer %X %X\n", phwi, dwTimeInterval);
#endif
  /* */
  /* Fail if the DSP kernel has been unloaded */
  /* */

  if (phwi->dwFlags & HWI_FLAG_UNLOADED)
    return KRETURN_ERROR_UNLOADED;

  /* */
  /* Check time interval size */
  /* */

  if (HIWORD (dwTimeInterval))
    return KRETURN_ERROR_GENERIC;

  /* */
  /* Write timer values */
  /* */

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA,
		 KDATA_TIMER_COUNT_RELOAD, (WORD) dwTimeInterval);

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA,
		 KDATA_TIMER_COUNT_CURRENT, (WORD) dwTimeInterval);

  return KRETURN_SUCCESS;

}				/* kSetTimer() */

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kOpenPassThru */
/* */
/*  Description: */
/*      Open a pass through. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      PPASSTHRU *ppPassThru */
/*         Pointer to PPASSTHRU that will contain the pass through pointer */
/* */
/*      DWORD dwDSPInConnection */
/*         KCONNECT_XXX indicating input connection */
/* */
/*      DWORD dwDSPOutConnection */
/*         KCONNECT_XXX indicating output connection */
/* */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */

KRETURN kOpenPassThru
  (allegro_devc * devc, PHWI phwi,
   PPASSTHRU * ppPassThru, DWORD dwDSPInConnection, DWORD dwDSPOutConnection)
{
  PPASSTHRU pPassThru = NULL;

#ifdef K_DBG
  _Debug_Printf_Service ("kOpenPassThru %X %X <-> %X\n", phwi,
			 dwDSPInConnection, dwDSPOutConnection);
#endif
  /* */
  /* Fail if the DSP kernel has been unloaded */
  /* */

  if (phwi->dwFlags & HWI_FLAG_UNLOADED)
    return KRETURN_ERROR_UNLOADED;

  /* */
  /* Make sure connections are valid... */
  /* */

  if ((dwDSPInConnection == KCONNECT_NONE) ||
      (dwDSPInConnection == KCONNECT_SAME) ||
      (dwDSPInConnection >= NUMBER_OF_CONNECTIONS))
    return KRETURN_ERROR_GENERIC;

  if ((dwDSPOutConnection == KCONNECT_NONE) ||
      (dwDSPOutConnection == KCONNECT_SAME) ||
      (dwDSPOutConnection >= NUMBER_OF_CONNECTIONS))
    return KRETURN_ERROR_GENERIC;

  /* */
  /* Do open/close synchronization */
  /* */

  if (!kStateExists (devc, phwi, KDATA_TASK_SWITCH, TRUE))
    return KRETURN_ERROR_BUSY;

  /* */
  /* Allocate host memory */
  /* */

  if (!(pPassThru = (PPASSTHRU)
	_HeapAllocate (sizeof (PASSTHRU), HEAPZEROINIT)))
    return KRETURN_ERROR_GENERIC;

  pPassThru->dwDSPInConnection = dwDSPInConnection;
  pPassThru->dwDSPOutConnection = dwDSPOutConnection;

  /* */
  /* Handle directly mixed (i.e. non-buffered) connections */
  /* */

  if (dwDSPOutConnection == KCONNECT_SPDIF)
    {
      kConnectDirectMixer (devc, phwi, dwDSPInConnection, dwDSPOutConnection,
			   TRUE);
    }
  else
    {
      if (dwDSPOutConnection == KCONNECT_MIXER)
	{
	  switch (dwDSPInConnection)
	    {
	    case KCONNECT_ADC1:
	      kDspWriteWord (devc, phwi->dwBaseIO,
			     MEMTYPE_INTERNAL_DATA,
			     KDATA_ADC1_MIXER_REQUEST, TRUE);
	      break;
	    case KCONNECT_ADC2:
	      kDspWriteWord (devc, phwi->dwBaseIO,
			     MEMTYPE_INTERNAL_DATA,
			     KDATA_ADC2_MIXER_REQUEST, TRUE);
	      break;

	    case KCONNECT_CD:
	      kDspWriteWord (devc, phwi->dwBaseIO,
			     MEMTYPE_INTERNAL_DATA,
			     KDATA_CD_MIXER_REQUEST, TRUE);
	      break;

	    case KCONNECT_MIC:
	      kDspWriteWord (devc, phwi->dwBaseIO,
			     MEMTYPE_INTERNAL_DATA,
			     KDATA_MIC_MIXER_REQUEST, TRUE);
	      break;

	    default:
	      goto kOpen_Error;
	    }			/* endswitch */
	}			/* endif */

    }

  /* */
  /* Everything worked */
  /* */

  *ppPassThru = pPassThru;

#ifdef K_DBG
  _Debug_Printf_Service ("kOpenPassThru OK %X\n", pPassThru);
#endif
  return KRETURN_SUCCESS;

kOpen_Error:

  /* */
  /* Failure, undo all that was done... */
  /* */

  if (pPassThru)
    {
      _HeapFree (pPassThru, 0);
    }

#ifdef K_DBG
  _Debug_Printf_Service ("kOpenPassThru Failed\n");
#endif
  return KRETURN_ERROR_GENERIC;

}				/* kOpenPassThru() */

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kClosePassThru */
/* */
/*  Description: */
/*      Close a pass through. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      PPASSTHRU pPassThru */
/*         Pointer to pass through */
/* */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */

KRETURN
kClosePassThru (allegro_devc * devc, PHWI phwi, PPASSTHRU pPassThru)
{
#ifdef K_DBG
  _Debug_Printf_Service ("kClosePassThru %X %X\n", phwi, pPassThru);
#endif
  /* */
  /* Fail if the DSP kernel has been unloaded */
  /* */

  if (phwi->dwFlags & HWI_FLAG_UNLOADED)
    return KRETURN_ERROR_UNLOADED;

  /* */
  /* Deallocate memory */
  /* */

  if (!pPassThru)
    return KRETURN_ERROR_GENERIC;

  /* */
  /* Handle directly mixed (i.e. non-buffered) connections */
  /* */

  if (pPassThru->dwDSPOutConnection == KCONNECT_SPDIF)
    {
      kConnectDirectMixer (devc, phwi,
			   pPassThru->dwDSPInConnection,
			   pPassThru->dwDSPOutConnection, FALSE);
    }
  else
    {
      if (pPassThru->dwDSPOutConnection == KCONNECT_MIXER)
	{
	  switch (pPassThru->dwDSPInConnection)
	    {
	    case KCONNECT_ADC1:
	      kDspWriteWord (devc, phwi->dwBaseIO,
			     MEMTYPE_INTERNAL_DATA,
			     KDATA_ADC1_MIXER_REQUEST, FALSE);
	      break;

	    case KCONNECT_ADC2:
	      kDspWriteWord (devc, phwi->dwBaseIO,
			     MEMTYPE_INTERNAL_DATA,
			     KDATA_ADC2_MIXER_REQUEST, FALSE);
	      break;

	    case KCONNECT_CD:
	      kDspWriteWord (devc, phwi->dwBaseIO,
			     MEMTYPE_INTERNAL_DATA,
			     KDATA_CD_MIXER_REQUEST, FALSE);
	      break;

	    case KCONNECT_MIC:
	      kDspWriteWord (devc, phwi->dwBaseIO,
			     MEMTYPE_INTERNAL_DATA,
			     KDATA_MIC_MIXER_REQUEST, FALSE);
	      break;

	    }			/* endswitch */

	}			/* endif */

    }

  _HeapFree (pPassThru, 0);

  /* */
  /* Do open/close synchronization */
  /* */

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA, KDATA_TASK_SWITCH, FALSE);

  return KRETURN_SUCCESS;

}				/* kClosePassThru() */

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kSetVolume */
/* */
/*  Description: */
/*      Set a client's current stream left/right Volume. */
/*      Maximum is 0x7FFF and Minimum is 0x0000. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      PCLIENT_INST pClient_Inst */
/*         Pointer to client instance */
/* */
/*      WORD wLeftVolume */
/*         16 bit constant for left channel volume */
/* */
/*      WORD wRightVolume */
/*         16 bit constant for right channel volume */
/* */
/*		WORD wBoosterMode */
/*		   TRUE activates a broader range of volume setting.   */
/*				In this mode, 7FFF represents 18dB gain */
/*							  1000 represents  0dB gain */
/*							     0 represents -Inf gain(mute)  */
/*		   FALSE supports only an attenuation mode. */
/*				In this mode, 7FFF represents  0dB gain				  	 */
/*								 0 represents -Inf gain(mute) */
/* */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */

KRETURN kSetVolume
  (allegro_devc * devc, PHWI phwi,
   PCLIENT_INST pClient_Inst,
   WORD wLeftVolume, WORD wRightVolume, WORD wBoosterMode)
{
  DWORD dwClient = pClient_Inst->dwClient;

#if 0
  WORD wCurLVol, wCurRVol, wLTemp, wRTemp;
#endif

#ifdef K_DBG
  _Debug_Printf_Service ("kSetVolume %X %X %X %X\n",
			 phwi, pClient_Inst, wLeftVolume, wRightVolume);
#endif
  /* */
  /* Fail if the DSP kernel has been unloaded */
  /* */

  if (phwi->dwFlags & HWI_FLAG_UNLOADED)
    return KRETURN_ERROR_UNLOADED;

  /* */
  /* Make sure client and an instance exist... */
  /* */

  if (dwClient >= NUMBER_OF_CLIENTS)
    return KRETURN_ERROR_GENERIC;

  if (!phwi->asClientTable[dwClient].dwReferenceCount)
    return KRETURN_ERROR_GENERIC;

#if 0				/* micro step the volume */
  wCurLVol = kDspReadWord (devc, phwi->dwBaseIO,
			   MEMTYPE_INTERNAL_DATA,
			   pClient_Inst->
			   dwDspDataClientArea + CDATA_LEFT_VOLUME);

  wCurRVol = kDspReadWord (devc, phwi->dwBaseIO,
			   MEMTYPE_INTERNAL_DATA,
			   pClient_Inst->
			   dwDspDataClientArea + CDATA_RIGHT_VOLUME);

  /* need to do micro steps to avoid zipper noise */
  /* max 7 steps */
  wLTemp = wLeftVolume;
  wRTemp = wRightVolume;


  wCurLVol &= 0xF000;
  wCurLVol |= (wLeftVolume & 0x0FFF);

  if (wCurLVol < wLeftVolume)
    {
      wLeftVolume = wCurLVol;
      wCurLVol = wLTemp;
    }				/* endif */

  do
    {

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     pClient_Inst->
		     dwDspDataClientArea + CDATA_LEFT_VOLUME, wCurLVol);

      kDelayNMicroseconds (10);
      wCurLVol -= 0x800;
    }
  while (wCurLVol > wLeftVolume);

  wCurRVol &= 0xF000;
  wCurRVol |= (wRightVolume & 0x0FFF);

  if (wCurRVol < wRightVolume)
    {
      wRightVolume = wCurRVol;
      wCurRVol = wRTemp;
    }				/* endif */

  do
    {

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     pClient_Inst->
		     dwDspDataClientArea + CDATA_RIGHT_VOLUME, wCurRVol);

      kDelayNMicroseconds (10);
      wCurRVol -= 0x800;
    }
  while (wCurRVol > wRightVolume);

  wLeftVolume = wLTemp;
  wRightVolume = wRTemp;
#endif
  /* */
  /* write left/right word of current stream volume */
  /* */

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA,
		 pClient_Inst->
		 dwDspDataClientArea + CDATA_LEFT_VOLUME, -wLeftVolume);

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA,
		 pClient_Inst->
		 dwDspDataClientArea + CDATA_RIGHT_VOLUME, -wRightVolume);

  /* */
  /* tell the client whether this is broader range  */
  /* of volume setting or not */
  /* */

  if (wBoosterMode)
    kDspWriteWord (devc, phwi->dwBaseIO,
		   MEMTYPE_INTERNAL_DATA,
		   pClient_Inst->
		   dwDspDataClientArea + CDATA_HEADER_LEN + 23, TRUE);
  else
    kDspWriteWord (devc, phwi->dwBaseIO,
		   MEMTYPE_INTERNAL_DATA,
		   pClient_Inst->
		   dwDspDataClientArea + CDATA_HEADER_LEN + 23, FALSE);


  return KRETURN_SUCCESS;

}				/* kSetVolume() */

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kSetRearVolume */
/* */
/*  Description: */
/*      Set a client's current stream rear left/rear right Volume. */
/*      Maximum is 0x7FFF and Minimum is 0x0000. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      PCLIENT_INST pClient_Inst */
/*         Pointer to client instance */
/* */
/*      WORD wLeftRearVolume */
/*         16 bit constant for rear left channel volume */
/* */
/*      WORD wRightRearVolume */
/*         16 bit constant for rear right channel volume */
/* */
/*		 */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */

KRETURN kSetRearVolume
  (allegro_devc * devc, PHWI phwi,
   PCLIENT_INST pClient_Inst, WORD wLeftRearVolume, WORD wRightRearVolume)
{
  DWORD dwClient = pClient_Inst->dwClient;


#ifdef K_DBG
  _Debug_Printf_Service ("kSetVolume %X %X %X %X\n",
			 phwi,
			 pClient_Inst, wLeftRearVolume, wRightRearVolume);
#endif
  /* */
  /* Fail if the DSP kernel has been unloaded */
  /* */

  if (phwi->dwFlags & HWI_FLAG_UNLOADED)
    return KRETURN_ERROR_UNLOADED;

  /* */
  /* Make sure client and an instance exist... */
  /* */

  if (dwClient >= NUMBER_OF_CLIENTS)
    return KRETURN_ERROR_GENERIC;

  if (!phwi->asClientTable[dwClient].dwReferenceCount)
    return KRETURN_ERROR_GENERIC;

  /* */
  /* write rear left/rear right word of current stream volume */
  /* */

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA,
		 pClient_Inst->
		 dwDspDataClientArea + CDATA_LEFT_SUR_VOL, wLeftRearVolume);

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA,
		 pClient_Inst->
		 dwDspDataClientArea + CDATA_RIGHT_SUR_VOL, wRightRearVolume);

  return KRETURN_SUCCESS;

}				/* kSetRearVolume() */

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kSetPassThruVolume */
/* */
/*  Description: */
/*      Set a passthrough instance volume. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      PPASSTHRU *ppPassThru */
/*         Pointer to PPASSTHRU that will contain the pass through pointer */
/* */
/*      WORD wLeftVolume */
/*         16 bit constant for left channel volume */
/* */
/*      WORD wRightVolume */
/*         16 bit constant for right channel volume */
/* */
/*	 Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */
KRETURN kSetPassThruVolume
  (allegro_devc * devc, PHWI phwi, PPASSTHRU pPassThru, WORD wLeftVolume,
   WORD wRightVolume)
{
  /* */
  /* Fail if the DSP kernel has been unloaded */
  /* */

  if (phwi->dwFlags & HWI_FLAG_UNLOADED)
    return KRETURN_ERROR_UNLOADED;

  /*  pPassThru -> wLeftVolume = wLeftVolume ; */
  /*  pPassThru -> wRightVolume = wRightVolume ; */

  switch (pPassThru->dwDSPInConnection)
    {
    case KCONNECT_ADC1:
      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     KDATA_ADC1_LEFT_VOLUME, wLeftVolume);

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     KDATA_ADC1_RIGHT_VOLUME, wRightVolume);

      break;

    case KCONNECT_ADC2:
      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     KDATA_ADC2_LEFT_VOLUME, wLeftVolume);

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     KDATA_ADC2_RIGHT_VOLUME, wRightVolume);

      break;

    case KCONNECT_CD:
      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     KDATA_CD_LEFT_VOLUME, wLeftVolume);

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     KDATA_CD_RIGHT_VOLUME, wRightVolume);
      break;

    case KCONNECT_MIC:
      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA, KDATA_MIC_VOLUME, wLeftVolume);

      break;

    default:
      return KRETURN_ERROR_GENERIC;
    }				/* endswitch */


  return KRETURN_SUCCESS;

}				/* kSetPassThruVolume */

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kSetPassThruRearVolume */
/* */
/*  Description: */
/*      Set a passthrough instance's rear volume. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      PPASSTHRU *ppPassThru */
/*         Pointer to PPASSTHRU that will contain the pass through pointer */
/* */
/*      WORD wLeftRearVolume */
/*         16 bit constant for left rear channel volume */
/* */
/*      WORD wRightRearVolume */
/*         16 bit constant for right rear channel volume */
/* */
/*	 Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */
KRETURN kSetPassThruRearVolume
  (allegro_devc * devc, PHWI phwi,
   PPASSTHRU pPassThru, WORD wLeftRearVolume, WORD wRightRearVolume)
{
  /* */
  /* Fail if the DSP kernel has been unloaded */
  /* */
  if (phwi->dwFlags & HWI_FLAG_UNLOADED)
    return KRETURN_ERROR_UNLOADED;

  switch (pPassThru->dwDSPInConnection)
    {
    case KCONNECT_ADC1:
      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     KDATA_ADC1_LEFT_SUR_VOL, wLeftRearVolume);

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     KDATA_ADC1_RIGHT_SUR_VOL, wRightRearVolume);

      break;


    case KCONNECT_ADC2:
      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     KDATA_ADC2_LEFT_SUR_VOL, wLeftRearVolume);

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     KDATA_ADC2_RIGHT_SUR_VOL, wRightRearVolume);

      break;

    case KCONNECT_CD:
      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     KDATA_CD_LEFT_SUR_VOL, wLeftRearVolume);

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     KDATA_CD_RIGHT_SUR_VOL, wRightRearVolume);
      break;

    case KCONNECT_MIC:
      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     KDATA_MIC_SUR_VOL, wLeftRearVolume);

      break;

    default:
      return KRETURN_ERROR_GENERIC;
    }				/* endswitch */


  return KRETURN_SUCCESS;

}				/* kSetPassThruRearVolume */


/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kSetMasterVolume */
/* */
/*  Description: */
/*      Set master volume. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      WORD wLeftVolume */
/*         16 bit constant for left channel volume */
/* */
/*      WORD wRightVolume */
/*         16 bit constant for right channel volume */
/* */
/*	 Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */
KRETURN
kSetMasterVolume (allegro_devc * devc, PHWI phwi, WORD wLeftVolume,
		  WORD wRightVolume)
{
  /* */
  /* Fail if the DSP kernel has been unloaded */
  /* */
  if (phwi->dwFlags & HWI_FLAG_UNLOADED)
    return KRETURN_ERROR_UNLOADED;

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA, KDATA_DAC_LEFT_VOLUME, wLeftVolume);

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA, KDATA_DAC_RIGHT_VOLUME, wRightVolume);

  return KRETURN_SUCCESS;

}				/* kSetMasterVolume */

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kSetFrequency */
/* */
/*  Description: */
/*      Set a client's current stream sampling frequency. */
/*      48khz is corresponding to 0x7FFF */
/*      0 hz is corresponding to 0x0000 */
/*      The other frequencies are linear mapping between those two */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      PCLIENT_INST pClient_Inst */
/*         Pointer to client instance */
/* */
/*      WORD wFrequency */
/*         16 bit constant for sampling frequency */
/* */
/* */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */

KRETURN
kSetFrequency (allegro_devc * devc, PHWI phwi, PCLIENT_INST pClient_Inst,
	       WORD wFrequency)
{
  DWORD dwClient = pClient_Inst->dwClient;
#ifdef K_DBG
  _Debug_Printf_Service ("kSetVolume %X %X %X\n", phwi,
			 pClient_Inst, wFrequency);
#endif


  /* */
  /* Fail if the DSP kernel has been unloaded */
  /* */

  if (phwi->dwFlags & HWI_FLAG_UNLOADED)
    return KRETURN_ERROR_UNLOADED;

  /* */
  /* Make sure client and an instance exist... */
  /* */

  if (dwClient >= NUMBER_OF_CLIENTS)
    return KRETURN_ERROR_GENERIC;

  if (!phwi->asClientTable[dwClient].dwReferenceCount)
    return KRETURN_ERROR_GENERIC;

  /* */
  /* write left/right word of current stream sampling frequency */
  /* */

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA,
		 pClient_Inst->
		 dwDspDataClientArea + CDATA_FREQUENCY, wFrequency);

  return KRETURN_SUCCESS;

}				/* kSetFrequency */


/**************************************************************************** */
/* The following is unique to M2/M2E only */
/**************************************************************************** */
#ifdef PIOREC
/*-------------------------------------------------------------------------- */
/* */
/*  DWORD kCopyLinearToCircular */
/* */
/*  Description: */
/*      Copy data from a linear DSP output buffer to a potentially */
/*      circular host destination buffer. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      DWORD dwMemAddr */
/*         Memory address to read from. */
/* */
/*      DWORD dwMemLen */
/*         Number of WORDs to read from specified address. */
/* */
/*      DWORD dwHostAddr */
/*         Host memory address to write to. */
/* */
/*      DWORD dwHostBase */
/*         Base address of host buffer. */
/* */
/*      DWORD dwHostLen */
/*         Length in bytes of host buffer. */
/* */
/*  Return (DWORD): */
/* */
/*-------------------------------------------------------------------------- */

DWORD kCopyLinearToCircular
  (allegro_devc * devc, PHWI phwi,
   DWORD dwMemAddr,
   DWORD dwMemLen, DWORD dwHostAddr, DWORD dwHostBase, DWORD dwHostLen)
{
  DWORD dwPreWrapLen;

  /* */
  /* copy DSP buffer data to host buffer */
  /* */

  if ((dwHostAddr + (dwMemLen * sizeof (WORD))) <= (dwHostBase + dwHostLen))
    {
      /* */
      /* host buffer won't wrap, do straight copy */
      /* */

      kDspReadWords (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     dwMemAddr, dwMemLen, (PWORD) dwHostAddr);
    }
  else
    {
      dwPreWrapLen = (dwHostBase + dwHostLen - dwHostAddr) / sizeof (WORD);

      /* */
      /* host buffer will wrap, do pre-wrap copy */
      /* */

      kDspReadWords (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     dwMemAddr, dwPreWrapLen, (PWORD) dwHostAddr);

      /* */
      /* do post-wrap copy */
      /* */

      kDspReadWords (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA,
		     dwMemAddr + dwPreWrapLen,
		     dwMemLen - dwPreWrapLen, (PWORD) dwHostBase);
    }

  /* */
  /* update current host pointer and check for wrap */
  /* */

  dwHostAddr += (dwMemLen * sizeof (WORD));

  if (dwHostAddr >= (dwHostBase + dwHostLen))
    {
      dwHostAddr = dwHostBase + (dwHostAddr - (dwHostBase + dwHostLen));
    }

  return dwHostAddr;

}				/* kCopyLinearToCircular() */

/*-------------------------------------------------------------------------- */
/* */
/*  VOID kPIOInterruptHandler */
/* */
/*  Description: */
/*      PIO data available interrupt handler. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      PCLIENT_INST pClient_Inst */
/*         Pointer to client instance */
/* */
/*  Return (VOID): */
/* */
/*-------------------------------------------------------------------------- */

VOID
kPIOInterruptHandler (allegro_devc * devc, PHWI phwi,
		      PCLIENT_INST pClient_Inst)
{
  DWORD dwHostDstBufferAddr = pClient_Inst->dwHostDstBufferAddr;
  DWORD dwHostDstBufferLen = pClient_Inst->dwHostDstBufferLen;
  DWORD dwHostDstCurrent = pClient_Inst->dwHostDstCurrent;
  DWORD dwDSPOutBufferAddr = pClient_Inst->dwDSPOutBufferAddr;
  DWORD dwDSPOutBufferLen = pClient_Inst->dwDSPOutBufferLen;
  DWORD dwOutBufHead;
  DWORD dwOutBufTail;

  /* */
  /* return if PIO recording not active */
  /* */

  if (!phwi->awVirtualPIOList[0])
    return;

  /* */
  /* read buffer state from DSP */
  /* */

  dwOutBufHead = kDspReadWord (devc, phwi->dwBaseIO,
			       MEMTYPE_INTERNAL_DATA,
			       pClient_Inst->dwDspDataClientArea +
			       CDATA_OUT_BUF_HEAD);

  dwOutBufTail = kDspReadWord (devc, phwi->dwBaseIO,
			       MEMTYPE_INTERNAL_DATA,
			       pClient_Inst->dwDspDataClientArea +
			       CDATA_OUT_BUF_TAIL);

  /* */
  /* copy DSP buffer data to host buffer */
  /* */

  if (dwOutBufHead > dwOutBufTail)
    {
      /* */
      /* DSP buffer data doesn't wrap, do straight copy */
      /* */

      dwHostDstCurrent =
	kCopyLinearToCircular (devc, phwi,
			       dwOutBufTail,
			       dwOutBufHead - dwOutBufTail,
			       dwHostDstCurrent,
			       dwHostDstBufferAddr, dwHostDstBufferLen);
    }
  else
    {
      /* */
      /* DSP buffer data wraps, do pre-wrap copy */
      /* */

      dwHostDstCurrent =
	kCopyLinearToCircular (devc, phwi,
			       dwOutBufTail,
			       dwDSPOutBufferAddr + (dwDSPOutBufferLen / 2)
			       - dwOutBufTail,
			       dwHostDstCurrent,
			       dwHostDstBufferAddr, dwHostDstBufferLen);

      /* */
      /* do post-wrap copy */
      /* */

      dwHostDstCurrent =
	kCopyLinearToCircular (devc, phwi,
			       dwDSPOutBufferAddr,
			       dwOutBufHead - dwDSPOutBufferAddr,
			       dwHostDstCurrent,
			       dwHostDstBufferAddr, dwHostDstBufferLen);
    }

  /* */
  /* write updated buffer state to DSP */
  /* */

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA,
		 pClient_Inst->dwDspDataClientArea + CDATA_OUT_BUF_TAIL,
		 (WORD) dwOutBufHead);

  pClient_Inst->dwHostDstCurrent = dwHostDstCurrent;

}				/* kPIOInterruptHandler() */
#endif

#ifdef HOSTI2SFREQCALC		/* I2S was calculated in the host */
#ifdef DOS_MODEL
#pragma message("----kI2SInterruptHandler omitted (causes internal compiler error)")
#else
/*-------------------------------------------------------------------------- */
/* */
/*  VOID kI2SInterruptHandler */
/* */
/*  Description: */
/*      I2S data flow impeded interrupt handler. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      PDWORD pdwI2SRate */
/*         Pointer to DWORD that will contain the I2S sample rate or */
/*         -1 if the sample rate should not be changed */
/* */
/*  Return (VOID): */
/* */
/*-------------------------------------------------------------------------- */

VOID
kI2SInterruptHandler (allegro_devc * devc, PHWI phwi, PDWORD pdwI2SRate)
{
  WORD wI2SSampleCount;
  WORD wI2STimerCount;
  WORD wI2SSampleRate;

  /* */
  /* Get current sample count and timer count */
  /* */

  wI2SSampleCount = kDspReadWord (devc, phwi->dwBaseIO,
				  MEMTYPE_INTERNAL_DATA,
				  KDATA_I2S_SAMPLE_COUNT);

  wI2STimerCount = kInW (devc, phwi->dwBaseIO + DSP_PORT_TIMER_COUNT);

  /* */
  /* I2S sample rate determination is a two pass operation. */
  /* On the first pass we zero the activity flag and return -1. */
  /* On the second pass we check if the activity flag is still */
  /* zero.  If so, we know the I2S data stream has stopped */
  /* and we return 0 for the sample rate.  Otherwise, we use */
  /* the sample count data and do our best to determine the */
  /* sample rate. */
  /* */

  if (phwi->dwFlags & HWI_FLAG_I2S_SECONDPASS)
    {
      if (!kDspReadWord (devc, phwi->dwBaseIO,
			 MEMTYPE_INTERNAL_DATA, KDATA_I2S_ACTIVE))
	{
	  /* */
	  /* the I2S stream has stopped */
	  /* */

	  *pdwI2SRate = 0;
	}
      else
	{
	  /* */
	  /* the I2S stream is moving */
	  /* */

	  wI2SSampleCount -= phwi->wI2SSampleCount;
	  wI2STimerCount -= phwi->wI2STimerCount;

	  /* */
	  /* determine sample rate */
	  /* */
	  /* The timer rate is 48,000 ticks/second, so */
	  /* */
	  /* samples   N samples   48,000 ticks      1 */
	  /* ------- = --------- X ------------ X ------- */
	  /*  second       1         1 second     M ticks */
	  /* */

	  wI2SSampleRate = (wI2SSampleCount * 48000) / wI2STimerCount;

	  if (wI2SSampleRate > (48000 + 44100) / 2)
	    *pdwI2SRate = 48000;
	  else if (wI2SSampleRate > (44100 + 32000) / 2)
	    *pdwI2SRate = 44100;
	  else if (wI2SSampleRate > (32000 + 22050) / 2)
	    *pdwI2SRate = 32000;
	  else
	    *pdwI2SRate = 22050;
	}

      phwi->dwFlags &= ~HWI_FLAG_I2S_SECONDPASS;
    }
  else
    {
      /* */
      /* zero the activity flag and advance to the next state */
      /* */

      kDspWriteWord (devc, phwi->dwBaseIO,
		     MEMTYPE_INTERNAL_DATA, KDATA_I2S_ACTIVE, 0);

      phwi->wI2SSampleCount = wI2SSampleCount;
      phwi->wI2STimerCount = wI2STimerCount;

      *pdwI2SRate = (DWORD) - 1;

      phwi->dwFlags |= HWI_FLAG_I2S_SECONDPASS;
    }

}				/* kI2SInterruptHandler() */
#endif
#endif

#ifdef APUBLOCKCOUNT
/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kResetApuBlockCount */
/* */
/*  Description: */
/*      Reset a client's APU block count field. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      PCLIENT_INST pClient_Inst */
/*         Pointer to client instance */
/* */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */

KRETURN
kResetApuBlockCount (allegro_devc * devc, PHWI phwi,
		     PCLIENT_INST pClient_Inst)
{
  DWORD dwClient = pClient_Inst->dwClient;

  /* */
  /* Fail if the DSP kernel has been unloaded */
  /* */

  if (phwi->dwFlags & HWI_FLAG_UNLOADED)
    return KRETURN_ERROR_UNLOADED;

  /* */
  /* Make sure client and an instance exist... */
  /* */

  if (dwClient >= NUMBER_OF_CLIENTS)
    return KRETURN_ERROR_GENERIC;

  if (!phwi->asClientTable[dwClient].dwReferenceCount)
    return KRETURN_ERROR_GENERIC;

  /* */
  /* Reset the block count */
  /* */

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA,
		 pClient_Inst->
		 dwDspDataClientArea + CDATA_APU_BLOCK_COUNTL, 0);

  kDspWriteWord (devc, phwi->dwBaseIO,
		 MEMTYPE_INTERNAL_DATA,
		 pClient_Inst->
		 dwDspDataClientArea + CDATA_APU_BLOCK_COUNTH, 0);

  return KRETURN_SUCCESS;

}				/* kResetApuBlockCount() */

/*-------------------------------------------------------------------------- */
/* */
/*  KRETURN kGetApuBlockCount */
/* */
/*  Description: */
/*      Return a client's current stream position in terms of the number */
/*      of APU blocks transfered. */
/* */
/*  Parameters: */
/*      PHWI phwi */
/*         Pointer to hardware instance. */
/* */
/*      PCLIENT_INST pClient_Inst */
/*         Pointer to client instance */
/* */
/*      PDWORD pdwBlockCount */
/*         Pointer to DWORD that will contain the number of APU blocks */
/* */
/*  Return (KRETURN): */
/*      KRETURN_SUCCESS if successful, KRETURN_ERROR_XXX otherwise. */
/* */
/*-------------------------------------------------------------------------- */

KRETURN kGetApuBlockCount
  (allegro_devc * devc, PHWI phwi, PCLIENT_INST pClient_Inst,
   PDWORD pdwBlockCount)
{
  DWORD dwClient = pClient_Inst->dwClient;
  WORD wBlockCount;
  WORD wRetry = 10;
  WORD wBlockCountL;
  WORD wBlockCountH;

  /* */
  /* Fail if the DSP kernel has been unloaded */
  /* */

  if (phwi->dwFlags & HWI_FLAG_UNLOADED)
    return KRETURN_ERROR_UNLOADED;

  /* */
  /* Make sure client and an instance exist... */
  /* */

  if (dwClient >= NUMBER_OF_CLIENTS)
    return KRETURN_ERROR_GENERIC;

  if (!phwi->asClientTable[dwClient].dwReferenceCount)
    return KRETURN_ERROR_GENERIC;

  /* */
  /* Get the block count */
  /* */

  while (wRetry--)
    {
      /* read high/low word of current block count */

      wBlockCountH = kDspReadWord (devc, phwi->dwBaseIO,
				   MEMTYPE_INTERNAL_DATA,
				   pClient_Inst->
				   dwDspDataClientArea +
				   CDATA_APU_BLOCK_COUNTH);

      wBlockCountL = kDspReadWord (devc, phwi->dwBaseIO,
				   MEMTYPE_INTERNAL_DATA,
				   pClient_Inst->
				   dwDspDataClientArea +
				   CDATA_APU_BLOCK_COUNTL);

      /* if the high word hasn't changed, we've got a meaningful */
      /* block count value */

      wBlockCount = kDspReadWord (devc, phwi->dwBaseIO,
				  MEMTYPE_INTERNAL_DATA,
				  pClient_Inst->
				  dwDspDataClientArea +
				  CDATA_APU_BLOCK_COUNTH);

      if (wBlockCount == wBlockCountH)
	break;
    }

  /* fail if we couldn't get a meaningful block count */

  if (wBlockCount != wBlockCountH)
    return KRETURN_ERROR_GENERIC;

  *pdwBlockCount = MAKELONG (wBlockCountL, wBlockCountH);

  return KRETURN_SUCCESS;

}				/* kGetApuBlockCount() */

#endif

/*--------------------------------------------------------------------------- */
/*  End of File: kernel.c */
/*--------------------------------------------------------------------------- */

/******************************************************************************
 *                                                                            *
 *                       (C) 1997-1999 ESS Technology, Inc.                   *
 *                                                                            *
 ******************************************************************************/
